diff --git a/apps/auth-service/.env.example b/apps/auth-service/.env.example index 186c404cf..e640e6bd8 100644 --- a/apps/auth-service/.env.example +++ b/apps/auth-service/.env.example @@ -1,3 +1,5 @@ +NODE_ENV= + PORT= CLERK_SECRET_KEY= diff --git a/apps/auth-service/Dockerfile b/apps/auth-service/Dockerfile index ea3bbf94d..f55a2c718 100644 --- a/apps/auth-service/Dockerfile +++ b/apps/auth-service/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine3.16 as base +FROM node:20-alpine3.16 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" diff --git a/apps/auth-service/package.json b/apps/auth-service/package.json index 8417c0cea..26f07c82b 100644 --- a/apps/auth-service/package.json +++ b/apps/auth-service/package.json @@ -1,6 +1,6 @@ { "name": "@codemod-com/auth-service", - "version": "0.0.15", + "version": "0.0.18", "scripts": { "build": "tsc && node esbuild.config.js", "start": "node build/index.js" @@ -21,11 +21,11 @@ "tsx": "^4.7.1" }, "dependencies": { - "@clerk/backend": "catalog:", - "@clerk/fastify": "catalog:", "@codemod-com/auth": "workspace:*", "@codemod-com/database": "workspace:*", "@codemod-com/utilities": "workspace:*", + "@clerk/backend": "catalog:", + "@clerk/fastify": "catalog:", "@fastify/busboy": "catalog:", "@fastify/cors": "catalog:", "@fastify/rate-limit": "catalog:", diff --git a/apps/backend/.env.example b/apps/backend/.env.example index 4588a1fa0..769c50d84 100644 --- a/apps/backend/.env.example +++ b/apps/backend/.env.example @@ -1,15 +1,18 @@ +NODE_ENV= + PORT= +ENCRYPTION_KEY= + AWS_SECRET_ACCESS_KEY= AWS_ACCESS_KEY_ID= +AWS_PUBLIC_BUCKET_NAME= +AWS_PRIVATE_BUCKET_NAME= +FRONTEND_URL= AUTH_SERVICE_URL= DATABASE_URI= -REDIS_HOST= -REDIS_PORT= -TASK_MANAGER_QUEUE_NAME= - POSTHOG_API_KEY= POSTHOG_PROJECT_ID= diff --git a/apps/backend/Dockerfile b/apps/backend/Dockerfile index eddd87484..b5bf61dc8 100644 --- a/apps/backend/Dockerfile +++ b/apps/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine3.16 as base +FROM node:20-alpine3.16 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" diff --git a/apps/backend/package.json b/apps/backend/package.json index 1a68f0caf..a09c4e853 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -1,6 +1,9 @@ { "name": "@codemod-com/backend", - "version": "0.0.158", + "version": "0.0.161", + "imports": { + "#*": "./src/*" + }, "scripts": { "build": "tsc && node esbuild.config.js", "start": "node build/index.js", @@ -31,12 +34,12 @@ "vitest": "1.1.0" }, "dependencies": { - "@aws-sdk/client-s3": "catalog:", - "@aws-sdk/s3-request-presigner": "catalog:", "@codemod-com/auth": "workspace:*", "@codemod-com/database": "workspace:*", "@codemod-com/runner": "workspace:*", "@codemod-com/utilities": "workspace:*", + "@aws-sdk/client-s3": "catalog:", + "@aws-sdk/s3-request-presigner": "catalog:", "@fastify/busboy": "catalog:", "@fastify/cors": "catalog:", "@fastify/multipart": "catalog:", diff --git a/apps/backend/src/handlers/codemod-runs/codemod-runs.get.ts b/apps/backend/src/handlers/codemod-runs/codemod-runs.get.ts new file mode 100644 index 000000000..1ec06a368 --- /dev/null +++ b/apps/backend/src/handlers/codemod-runs/codemod-runs.get.ts @@ -0,0 +1,34 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { prisma } from "@codemod-com/database"; +import type { CodemodRun } from "@codemod-com/database"; +import type { RouteHandler } from "fastify"; +import { parsePaginatedGetQuery } from "#schemata/schema.js"; + +export type GetCodemodRunsResponse = ApiResponse<{ + total: number; + data: CodemodRun[]; +}>; + +export const getCodemodRunsHandler: RouteHandler<{ + Reply: GetCodemodRunsResponse; +}> = async (request: UserDataPopulatedRequest) => { + const query = parsePaginatedGetQuery(request.query); + + const userId = request.user?.id; + const [total, codemodRuns] = await Promise.all([ + prisma.codemodRun.count({ + where: { ownerId: userId }, + }), + prisma.codemodRun.findMany({ + where: { ownerId: userId }, + take: query.size, + skip: query.size && query.page ? query.size * (query.page - 1) : 0, + }), + ]); + + return { + total, + data: codemodRuns, + }; +}; diff --git a/apps/backend/src/handlers/getCodemodsListHandler.ts b/apps/backend/src/handlers/codemods/codemod-list.get.ts similarity index 87% rename from apps/backend/src/handlers/getCodemodsListHandler.ts rename to apps/backend/src/handlers/codemods/codemod-list.get.ts index aba1ae2aa..f41f46ea0 100644 --- a/apps/backend/src/handlers/getCodemodsListHandler.ts +++ b/apps/backend/src/handlers/codemods/codemod-list.get.ts @@ -5,9 +5,9 @@ import type { RouteHandler } from "fastify"; import { parseClientIdentifierSchema, parseListCodemodsQuery, -} from "../schemata/schema.js"; -import { codemodService } from "../services/CodemodService.js"; -import { telemetryService } from "../services/TelemetryService.js"; +} from "#schemata/schema.js"; +import { codemodService } from "#services/CodemodService.js"; +import { telemetryService } from "#services/TelemetryService.js"; export const getCodemodsListHandler: RouteHandler<{ Reply: CodemodListResponse; diff --git a/apps/backend/src/handlers/getCodemodHandler.ts b/apps/backend/src/handlers/codemods/codemod.get.ts similarity index 76% rename from apps/backend/src/handlers/getCodemodHandler.ts rename to apps/backend/src/handlers/codemods/codemod.get.ts index e8ed9bb29..56c82f214 100644 --- a/apps/backend/src/handlers/getCodemodHandler.ts +++ b/apps/backend/src/handlers/codemods/codemod.get.ts @@ -1,9 +1,9 @@ import type { ApiResponse, GetCodemodResponse } from "@codemod-com/api-types"; import type { UserDataPopulatedRequest } from "@codemod-com/auth"; import type { RouteHandler } from "fastify"; -import { parseGetCodemodBySlugParams } from "../schemata/schema.js"; -import { codemodService } from "../services/CodemodService.js"; -import { processHandlerError } from "../types/errors.js"; +import { parseGetCodemodBySlugParams } from "#schemata/schema.js"; +import { codemodService } from "#services/CodemodService.js"; +import { processHandlerError } from "#types/errors.js"; export const getCodemodHandler: RouteHandler<{ Reply: ApiResponse; diff --git a/apps/backend/src/handlers/getCodemodsHandler.ts b/apps/backend/src/handlers/codemods/codemods.get.ts similarity index 87% rename from apps/backend/src/handlers/getCodemodsHandler.ts rename to apps/backend/src/handlers/codemods/codemods.get.ts index b5954fae8..75f7801c2 100644 --- a/apps/backend/src/handlers/getCodemodsHandler.ts +++ b/apps/backend/src/handlers/codemods/codemods.get.ts @@ -2,8 +2,8 @@ import type { ApiResponse } from "@codemod-com/api-types"; import type { UserDataPopulatedRequest } from "@codemod-com/auth"; import type { Codemod } from "@codemod-com/database"; import type { RouteHandler } from "fastify"; -import { parseGetCodemodsQuery } from "../schemata/schema.js"; -import { codemodService } from "../services/CodemodService.js"; +import { parseGetCodemodsQuery } from "#schemata/schema.js"; +import { codemodService } from "#services/CodemodService.js"; export type GetCodemodsResponse = ApiResponse<{ total: number; diff --git a/apps/backend/src/handlers/getCodemodDownloadLink.ts b/apps/backend/src/handlers/codemods/download-link.get.ts similarity index 88% rename from apps/backend/src/handlers/getCodemodDownloadLink.ts rename to apps/backend/src/handlers/codemods/download-link.get.ts index 4e058ec02..344bc5a4c 100644 --- a/apps/backend/src/handlers/getCodemodDownloadLink.ts +++ b/apps/backend/src/handlers/codemods/download-link.get.ts @@ -6,10 +6,10 @@ import type { } from "@codemod-com/api-types"; import type { UserDataPopulatedRequest } from "@codemod-com/auth"; import type { FastifyReply, RouteHandler } from "fastify"; -import { parseGetCodemodLatestVersionQuery } from "../schemata/schema.js"; -import { codemodService } from "../services/CodemodService.js"; -import { processHandlerError } from "../types/errors.js"; -import { environment } from "../util.js"; +import { parseGetCodemodLatestVersionQuery } from "#schemata/schema.js"; +import { codemodService } from "#services/CodemodService.js"; +import { processHandlerError } from "#types/errors.js"; +import { environment } from "#util.js"; export type GetCodemodDownloadLinkResponse = ApiResponse; diff --git a/apps/backend/src/handlers/insights/insight.get.ts b/apps/backend/src/handlers/insights/insight.get.ts new file mode 100644 index 000000000..163152063 --- /dev/null +++ b/apps/backend/src/handlers/insights/insight.get.ts @@ -0,0 +1,35 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { prisma } from "@codemod-com/database"; +import type { Insight, Widget } from "@codemod-com/database"; +import type { FastifyReply, RouteHandler } from "fastify"; +import * as v from "valibot"; + +export type GetInsightResponse = ApiResponse; + +export const getInsightHandler: RouteHandler<{ + Reply: GetInsightResponse; +}> = async (request: UserDataPopulatedRequest, reply: FastifyReply) => { + const { id } = v.parse( + v.object({ id: v.pipe(v.string(), v.transform(Number), v.number()) }), + request.params, + ); + + const insight = await prisma.insight.findFirst({ + where: { id }, + include: { + widgets: true, + codemodRuns: { select: { data: true, repoUrl: true, branch: true } }, + }, + }); + + if (!insight) { + return reply.status(400).send({ + // @TODO + error: "INSIGHT_NOT_FOUND", + errorText: "Insight not found", + }); + } + + return insight; +}; diff --git a/apps/backend/src/handlers/insights/insight.put.ts b/apps/backend/src/handlers/insights/insight.put.ts new file mode 100644 index 000000000..981cc475c --- /dev/null +++ b/apps/backend/src/handlers/insights/insight.put.ts @@ -0,0 +1,127 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { prisma } from "@codemod-com/database"; +import type { Insight } from "@codemod-com/database"; +import { + type CodemodRunResponse, + parsePutInsightBody, +} from "@codemod-com/utilities"; +import axios from "axios"; +import type { FastifyReply, RouteHandler } from "fastify"; +import { environment } from "#util.js"; + +const DEFAULT_CODEMODS = [ + { + engine: "workflow", + name: "drift_analyzer", + }, + { + engine: "workflow", + name: "drift_analyzer_pkg", + }, +]; + +export type PutInsightResponse = ApiResponse; + +export const putInsightHandler: RouteHandler<{ + Reply: PutInsightResponse; +}> = async (request: UserDataPopulatedRequest, reply: FastifyReply) => { + // try + const body = parsePutInsightBody(request.body); + // return reply.status(400).send({ + // // @TODO + // error: "INVALID_INSIGHT", + // errorText: "Invalid insight", + // }); + + try { + if ("id" in body) { + const insight = await prisma.insight + .update({ where: { id: body.id }, data: body }) + .catch(() => null); + + if (body.repoUrls.length > 0) { + await Promise.all( + body.repoUrls.map(async (repoUrl) => { + const { data: response } = await axios.post( + `${environment.RUN_SERVICE_URL}/codemodRun`, + { + repoUrl, + codemods: DEFAULT_CODEMODS, + }, + { + headers: { + Authorization: request.headers?.authorization, + }, + }, + ); + + const { data } = response; + + for (const codemodRunResult of data) { + await prisma.codemodRun.update({ + where: { jobId: codemodRunResult.jobId }, + data: { + insight: { connect: { id: body.id } }, + }, + }); + } + }), + ); + } + + if (insight === null) { + return reply.status(400).send({ + // @TODO + error: "INSIGHT_NOT_FOUND", + errorText: "Insight not found", + }); + } + + return insight; + } + + const insight = await prisma.insight.create({ + data: { + ownerId: request.user!.id, + repoUrls: body.repoUrls, + }, + }); + + await Promise.all( + body.repoUrls.map(async (repoUrl) => { + const { data: response } = await axios.post( + `${environment.RUN_SERVICE_URL}/codemodRun`, + { + repoUrl, + codemods: DEFAULT_CODEMODS, + }, + { + headers: { + Authorization: request.headers?.authorization, + }, + }, + ); + + const { data } = response; + + for (const codemodRunResult of data) { + await prisma.codemodRun.update({ + where: { jobId: codemodRunResult.jobId }, + data: { + insight: { connect: { id: insight.id } }, + }, + }); + } + }), + ); + + return insight; + } catch (err) { + return reply.status(400).send({ + // @TODO + error: "INSIGHT_NOT_FOUND", + errorText: "Insight not found", + }); + } +}; diff --git a/apps/backend/src/handlers/insights/insights-new.post.ts b/apps/backend/src/handlers/insights/insights-new.post.ts new file mode 100644 index 000000000..e48c4ba0c --- /dev/null +++ b/apps/backend/src/handlers/insights/insights-new.post.ts @@ -0,0 +1,50 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { type Insight, prisma } from "@codemod-com/database"; +import { + type CodemodRunResponse, + parsePostInsightBody, +} from "@codemod-com/utilities"; +import axios from "axios"; +import type { FastifyReply, RouteHandler } from "fastify"; +import { environment } from "#util.js"; + +const DEFAULT_CODEMODS = [ + { engine: "workflow", name: "drift_analyzer" }, + { engine: "workflow", name: "drift_analyzer_pkg" }, +]; + +export type PostNewInsightResponse = ApiResponse; + +export const postNewInsightHandler: RouteHandler<{ + Reply: PostNewInsightResponse; +}> = async (request: UserDataPopulatedRequest, reply: FastifyReply) => { + const { repoUrls } = parsePostInsightBody(request.body); + + const insight = await prisma.insight.create({ + data: { ownerId: request.user!.id, repoUrls }, + }); + + await Promise.all( + repoUrls.map(async (repoUrl) => { + const { data: response } = await axios.post( + `${environment.RUN_SERVICE_URL}/codemodRun`, + { repoUrl, codemods: DEFAULT_CODEMODS }, + { headers: { Authorization: request.headers?.authorization } }, + ); + + console.dir({ response }, { depth: 8 }); + + const { data } = response; + + for (const codemodRunResult of data) { + await prisma.codemodRun.update({ + where: { jobId: codemodRunResult.jobId }, + data: { insight: { connect: { id: insight.id } } }, + }); + } + }), + ); + + return insight; +}; diff --git a/apps/backend/src/handlers/insights/insights.get.ts b/apps/backend/src/handlers/insights/insights.get.ts new file mode 100644 index 000000000..dd02341bc --- /dev/null +++ b/apps/backend/src/handlers/insights/insights.get.ts @@ -0,0 +1,35 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { prisma } from "@codemod-com/database"; +import type { Insight } from "@codemod-com/database"; +import type { RouteHandler } from "fastify"; +import { parsePaginatedGetQuery } from "#schemata/schema.js"; + +export type GetInsightsResponse = ApiResponse<{ + total: number; + data: (Omit & { loading: boolean })[]; +}>; + +export const getInsightsHandler: RouteHandler<{ + Reply: GetInsightsResponse; +}> = async (request: UserDataPopulatedRequest) => { + const query = parsePaginatedGetQuery(request.query); + + const insights = await prisma.insight.findMany({ + include: { codemodRuns: { select: { data: true } } }, + take: query.size, + skip: query.size && query.page ? query.size * (query.page - 1) : 0, + }); + + return { + total: 450, + data: insights.map((insight) => { + return { + ...insight, + loading: insight.codemodRuns.some( + (run) => run.data.status === "progress", + ), + }; + }), + }; +}; diff --git a/apps/backend/src/handlers/widgets/widget.put.ts b/apps/backend/src/handlers/widgets/widget.put.ts new file mode 100644 index 000000000..eec05cf9c --- /dev/null +++ b/apps/backend/src/handlers/widgets/widget.put.ts @@ -0,0 +1,55 @@ +import type { ApiResponse } from "@codemod-com/api-types"; +import type { UserDataPopulatedRequest } from "@codemod-com/auth"; +import { prisma } from "@codemod-com/database"; +import type { Widget } from "@codemod-com/database"; +import { parsePutWidgetBody } from "@codemod-com/utilities"; +import type { FastifyReply, RouteHandler } from "fastify"; + +export type PutWidgetResponse = ApiResponse; + +export const putWidgetHandler: RouteHandler<{ + Reply: PutWidgetResponse; +}> = async (request: UserDataPopulatedRequest, reply: FastifyReply) => { + // try + const body = parsePutWidgetBody(request.body); + // return reply.status(400).send({ + // // @TODO + // error: "INVALID_WIDGET", + // errorText: "Invalid widget", + // }); + + try { + if ("id" in body) { + const widget = await prisma.widget + .update({ where: { id: body.id }, data: body }) + .catch(() => null); + + if (widget === null) { + return reply.status(400).send({ + // @TODO + error: "WIDGET_NOT_FOUND", + errorText: "Widget not found", + }); + } + + return widget; + } + + const widget = await prisma.widget.create({ + data: { + title: body.title, + data: body.data, + kind: body.kind, + insight: { connect: { id: body.insightId } }, + }, + }); + + return widget; + } catch (err) { + return reply.status(400).send({ + // @TODO + error: "WIDGET_NOT_FOUND", + errorText: "Widget not found", + }); + } +}; diff --git a/apps/backend/src/schemata/schema.ts b/apps/backend/src/schemata/schema.ts index 8edf48737..ba7bf9a89 100644 --- a/apps/backend/src/schemata/schema.ts +++ b/apps/backend/src/schemata/schema.ts @@ -2,7 +2,6 @@ import { codemodRunBodySchema, validateCodemodStatusParamsSchema, } from "@codemod-com/utilities"; - import { array, boolean, @@ -91,6 +90,13 @@ export const parseGetRepoBranchesBody = (input: unknown) => export const parseGetRepoBranchesParams = (input: unknown) => parse(providerSchema, input); +export const paginatedQuerySchema = object({ + page: optional(pipe(string(), transform(Number))), + size: optional(pipe(string(), transform(Number))), +}); +export const parsePaginatedGetQuery = (input: unknown) => + parse(paginatedQuerySchema, input); + export const getCodemodsQuerySchema = object({ search: optional(string()), category: optional(union([string(), array(string())])), diff --git a/apps/backend/src/server.ts b/apps/backend/src/server.ts index 33137d4d4..c50ba647c 100644 --- a/apps/backend/src/server.ts +++ b/apps/backend/src/server.ts @@ -11,15 +11,39 @@ import Fastify, { type FastifyRequest, } from "fastify"; import { - type GetCodemodDownloadLinkResponse, - getCodemodDownloadLink, -} from "./handlers/getCodemodDownloadLink.js"; -import { getCodemodHandler } from "./handlers/getCodemodHandler.js"; + type PutInsightResponse, + putInsightHandler, +} from "#handlers/insights/insight.put.js"; +import { + type PostNewInsightResponse, + postNewInsightHandler, +} from "#handlers/insights/insights-new.post.js"; +import { + type GetCodemodRunsResponse, + getCodemodRunsHandler, +} from "./handlers/codemod-runs/codemod-runs.get.js"; +import { getCodemodsListHandler } from "./handlers/codemods/codemod-list.get.js"; +import { getCodemodHandler } from "./handlers/codemods/codemod.get.js"; import { type GetCodemodsResponse, getCodemodsHandler, -} from "./handlers/getCodemodsHandler.js"; -import { getCodemodsListHandler } from "./handlers/getCodemodsListHandler.js"; +} from "./handlers/codemods/codemods.get.js"; +import { + type GetCodemodDownloadLinkResponse, + getCodemodDownloadLink, +} from "./handlers/codemods/download-link.get.js"; +import { + type GetInsightResponse, + getInsightHandler, +} from "./handlers/insights/insight.get.js"; +import { + type GetInsightsResponse, + getInsightsHandler, +} from "./handlers/insights/insights.get.js"; +import { + type PutWidgetResponse, + putWidgetHandler, +} from "./handlers/widgets/widget.put.js"; import { type PublishHandlerResponse, publishHandler, @@ -349,6 +373,43 @@ const routes: FastifyPluginCallback = (instance, _opts, done) => { return result; }, ); + + instance.get<{ Reply: GetInsightsResponse }>( + "/insights", + { preHandler: [instance.getUserData] }, + getInsightsHandler, + ); + + instance.get<{ Reply: GetInsightResponse }>( + "/insights/:id", + { preHandler: [instance.getUserData] }, + getInsightHandler, + ); + + instance.post<{ Reply: PostNewInsightResponse }>( + "/insights/new", + { preHandler: [instance.getUserData] }, + postNewInsightHandler, + ); + + instance.put<{ Reply: PutInsightResponse }>( + "/insights", + { preHandler: [instance.getUserData] }, + putInsightHandler, + ); + + instance.get<{ Reply: GetCodemodRunsResponse }>( + "/codemod-runs", + { preHandler: [instance.getUserData] }, + getCodemodRunsHandler, + ); + + instance.put<{ Reply: PutWidgetResponse }>( + "/widget", + { preHandler: [instance.getUserData] }, + putWidgetHandler, + ); + done(); }; diff --git a/apps/cli/package.json b/apps/cli/package.json index 9f4b0e6fe..ee72f8183 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -4,7 +4,7 @@ "imports": { "#*": "./src/*" }, - "version": "0.13.11", + "version": "0.14.1", "description": "A codemod engine for Node.js libraries (jscodeshift, ts-morph, etc.)", "type": "module", "exports": null, diff --git a/apps/cli/src/commands/run.ts b/apps/cli/src/commands/run.ts index 89c0c7af3..d28169aa3 100644 --- a/apps/cli/src/commands/run.ts +++ b/apps/cli/src/commands/run.ts @@ -94,7 +94,6 @@ export const handleRunCliCommand = async (options: { if (args.mode === "json") { process.stdout.write = () => false; - process.stderr.write = () => false; } const flowSettings = await parseFlowSettings(args, printer); diff --git a/apps/frontend/app/(website)/auth/layout.tsx b/apps/frontend/app/(website)/auth/layout.tsx deleted file mode 100644 index ced653703..000000000 --- a/apps/frontend/app/(website)/auth/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import AuthProvider from "@/app/context/AuthProvider"; - -export default async function Layout({ - children, -}: { - children: React.ReactNode; -}) { - return {children}; -} diff --git a/apps/frontend/app/(website)/auth/sign-in/[[...param]]/page.tsx b/apps/frontend/app/(website)/auth/sign-in/[[...param]]/page.tsx index 6ec319079..334380041 100644 --- a/apps/frontend/app/(website)/auth/sign-in/[[...param]]/page.tsx +++ b/apps/frontend/app/(website)/auth/sign-in/[[...param]]/page.tsx @@ -11,7 +11,7 @@ function SignInPage() { return (
diff --git a/apps/frontend/app/(website)/insights/[insightId]/chartColorSets.ts b/apps/frontend/app/(website)/insights/[insightId]/chartColorSets.ts new file mode 100644 index 000000000..ff2e91394 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/chartColorSets.ts @@ -0,0 +1,26 @@ +import type { + ChartType, + ColorConfig, +} from "@/app/(website)/insights/[insightId]/types"; + +export const chartColorSets: Record = { + depreciatedAPI: [ + { + line: "#60A5FA", + gradientStart: "#93C5FD", + gradientEnd: "#EFF6FF", + }, + { + line: "#4ADE80", + gradientStart: "#86EFAC", + gradientEnd: "#ECFDF5", + }, + ], + prsMerged: [ + { + line: "#F59E0B", + gradientStart: "#FCD34D", + gradientEnd: "#FFFBEB", + }, + ], +}; diff --git a/apps/frontend/app/(website)/insights/[insightId]/hooks/useCodemodRunExecutionStatus.ts b/apps/frontend/app/(website)/insights/[insightId]/hooks/useCodemodRunExecutionStatus.ts new file mode 100644 index 000000000..99b2accf4 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/hooks/useCodemodRunExecutionStatus.ts @@ -0,0 +1,76 @@ +import { GET_EXECUTION_STATUS } from "@mocks/endpoints/gh-run"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useEffect, useState } from "react"; + +const DEFAULT_POLLING_INTERVAL = 1000; + +export type GetExecutionStatusResponse = Array< + | { + status: "queued" | "in_progress" | "errored"; + message: string; + codemod: string; + progress: number; + id: string; + } + | { + status: "success"; + result: string; + codemod: string; + progress: number; + message: string; + id: string; + } +>; + +export const useCodemodRunResult = ( + executionIds: string[], + pollingInterval = DEFAULT_POLLING_INTERVAL, +): GetExecutionStatusResponse => { + const [executionStatuses, setExecutionStatuses] = + useState([]); + + const { get: getExecutionStatus } = useAPI( + GET_EXECUTION_STATUS(executionIds), + ); + + useEffect(() => { + if (executionIds.length === 0) { + return; + } + + let intervalId: number; + + const pollCodemodRunExecutionStatus = async () => { + intervalId = window.setInterval(async () => { + let response: GetExecutionStatusResponse | null = null; + + try { + response = (await getExecutionStatus()).data; + } catch (e) { + clearInterval(intervalId); + return; + } + + setExecutionStatuses(response); + + const areAllCodemodsExecutedOrErrored = response.every(({ status }) => + ["success", "errored"].includes(status), + ); + + if (areAllCodemodsExecutedOrErrored) { + clearInterval(intervalId); + } + }, pollingInterval); + }; + + pollCodemodRunExecutionStatus(); + + return () => { + clearInterval(intervalId); + }; + + // @TODO why + }, [executionIds.join(",")]); + + return executionStatuses; +}; diff --git a/apps/frontend/app/(website)/insights/[insightId]/hooks/useRunCodemodMutation.ts b/apps/frontend/app/(website)/insights/[insightId]/hooks/useRunCodemodMutation.ts new file mode 100644 index 000000000..e98441970 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/hooks/useRunCodemodMutation.ts @@ -0,0 +1,28 @@ +import type { + CodemodRunBody, + CodemodRunResponse, +} from "@codemod-com/utilities"; +import { RUN_CODEMOD as RUN_CODEMOD_URL } from "@mocks/endpoints/gh-run"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useState } from "react"; +import { useMutation } from "react-query"; + +export const useRunCodemodMutation = () => { + const [executionIds, setExecutionIds] = useState([]); + + const { post: runCodemod } = useAPI(RUN_CODEMOD_URL); + + const runCodemodMutation = useMutation({ + mutationFn: async ( + request: CodemodRunBody, + ): Promise => { + const { data: result } = await runCodemod(request); + + setExecutionIds(result.data.map(({ jobId }) => jobId)); + + return result.data; + }, + }); + + return { executionIds, runCodemodMutation }; +}; diff --git a/apps/frontend/app/(website)/insights/[insightId]/hooks/useStorage.ts b/apps/frontend/app/(website)/insights/[insightId]/hooks/useStorage.ts new file mode 100644 index 000000000..665133ac3 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/hooks/useStorage.ts @@ -0,0 +1,57 @@ +import { useEffect, useState } from "react"; + +interface DashboardComponentData { + name: string; + data: T; + componentName: string; + config: any; +} + +function useStorage( + name: string, + componentName: string, + initialData: T, + initialConfig?: any, +): [T, (data: T) => void] { + const storageKey = `${name}-${componentName}`; + + const [state, setState] = useState(() => { + const storedData = localStorage.getItem(storageKey); + if (storedData) { + try { + return JSON.parse(storedData).data; + } catch (e) { + localStorage.removeItem(storageKey); + } + } + return initialData; + }); + + useEffect(() => { + const storedData = localStorage.getItem(storageKey); + if (!storedData) { + const initialState: DashboardComponentData = { + name, + data: initialData, + componentName, + config: initialConfig, + }; + localStorage.setItem(storageKey, JSON.stringify(initialState)); + } + }, [name, componentName, initialData, initialConfig, storageKey]); + + const setStorage = (newData: T) => { + setState(newData); + const updatedState: DashboardComponentData = { + name, + data: newData, + componentName, + config: initialConfig, + }; + localStorage.setItem(storageKey, JSON.stringify(updatedState)); + }; + + return [state, setStorage]; +} + +export default useStorage; diff --git a/apps/frontend/app/(website)/insights/[insightId]/mockData.tsx b/apps/frontend/app/(website)/insights/[insightId]/mockData.tsx new file mode 100644 index 000000000..94f56a1ac --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/mockData.tsx @@ -0,0 +1,481 @@ +import type { + DataPoint, + MigrationPrTable, + MigrationPrTableProps, + TableTileProps, +} from "@/app/(website)/insights/[insightId]/view/[viewId]/types"; + +const currentDate = new Date(); +export type CardTileData = { + title: string; + value: string | number; + change: number; + subtitle: string; +}; + +export type CardDataObject = { + [key: string]: CardTileData; +}; +export const cardData: CardDataObject = { + timeSaving: { + title: "Estimated time saving", + value: "PT130H30M", + change: 23, + subtitle: "more than previous 3 months", + }, + reviewTime: { + title: "Review time for auto-generated PRs", + value: "PT1M30S", // ISO 8601 duration format + change: -16, + subtitle: "slower than previous 3 months", + }, + mergedPRs: { + title: "Auto-generated PRs merged", + value: 106, + change: 10, + subtitle: "compared to previous 3 months", + }, +}; + +const driftDataArray: DataPoint[] = [ + { + kind: "real_drift", + package: "test-package", + timestamp: currentDate.getTime(), + value: 0, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 86400000).getTime(), // +1 day + value: 8.046708693539223, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 2 * 86400000).getTime(), // +2 days + value: 8.046708693539223, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 3 * 86400000).getTime(), // +3 days + value: 16.112582736127365, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 4 * 86400000).getTime(), // +4 days + value: 16.112582736127365, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 5 * 86400000).getTime(), // +5 days + value: 20.613701855616473, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 6 * 86400000).getTime(), // +6 days + value: 20.613701855616473, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 7 * 86400000).getTime(), // +7 days + value: 20.613701855616473, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 8 * 86400000).getTime(), // +8 days + value: 20.613701855616473, + }, + { + kind: "real_drift", + package: "test-package", + timestamp: new Date(currentDate.getTime() + 9 * 86400000).getTime(), // +9 days + value: 16.41922832091008, + }, +]; + +export const customData = [ + { + title: "useEffectLegacy", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + ], + }, + { + title: "useCallbackModern", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + ], + }, + { + title: "useMemoOptimized", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 20) + 1, + }, + ], + }, +]; + +export const depreciatedAPIData = [ + { + title: "useContextReact18", + data: [ + { timestamp: new Date("2023-06-01").getTime(), value: 18 }, + { timestamp: new Date("2023-07-01").getTime(), value: 10 }, + { timestamp: new Date("2023-08-01").getTime(), value: 2 }, + ], + }, + { + title: "useReact19", + data: [ + { timestamp: new Date("2023-06-01").getTime(), value: 2 }, + { timestamp: new Date("2023-07-01").getTime(), value: 10 }, + { timestamp: new Date("2023-08-01").getTime(), value: 18 }, + ], + }, +]; + +export const prsMergedData = [ + { + title: "removeMemoizationHooks", + data: [ + { timestamp: new Date("2023-06-01").getTime(), value: 5 }, + { timestamp: new Date("2023-07-01").getTime(), value: 15 }, + { timestamp: new Date("2023-08-01").getTime(), value: 12 }, + ], + }, +]; + +export const tableData: TableTileProps = { + title: "React 18 to 19 Migration PRs", + data: [ + { + task: "Linear - CDMD 1253", + pr: "codemod-dd-312837761", + status: "In Review", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/78830094?s=16&v=4", + name: "codemod", + }, + filesChanged: 55, + timeSaving: "13h 35m", + }, + { + task: "Linear - CDMD 1252", + pr: "codemod-dd-2516551", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/78830094?s=16&v=4", + name: "codemod", + }, + filesChanged: 12, + timeSaving: "9h 00m", + }, + { + task: "Linear - CDMD 1251", + pr: "codemod-dd-321-565", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/78830094?s=16&v=4", + name: "codemod", + }, + filesChanged: 6, + timeSaving: "5h 35m", + }, + ], +}; + +export const migrationPrData: MigrationPrTableProps = { + title: "React 18 to 19 Migration PRs", + data: [ + { + task: "Linear - CDMD 1253", + pr: "codemod-dd-312837761", + status: "In Review", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/78830094?s=16&v=4", + name: "Alex", + }, + filesChanged: 55, + timeSaving: "13h 35m", + }, + { + task: "Linear - CDMD 1252", + pr: "codemod-dd-2516551", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/12345678?s=16&v=4", + name: "Emma", + }, + filesChanged: 12, + timeSaving: "9h 00m", + }, + { + task: "Linear - CDMD 1251", + pr: "codemod-dd-321-565", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/87654321?s=16&v=4", + name: "Michael", + }, + filesChanged: 6, + timeSaving: "5h 35m", + }, + { + task: "Linear - CDMD 1250", + pr: "codemod-dd-987654", + status: "In Review", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/23456789?s=16&v=4", + name: "Sophia", + }, + filesChanged: 23, + timeSaving: "7h 45m", + }, + { + task: "Linear - CDMD 1249", + pr: "codemod-dd-456789", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/34567890?s=16&v=4", + name: "Daniel", + }, + filesChanged: 8, + timeSaving: "3h 20m", + }, + { + task: "Linear - CDMD 1248", + pr: "codemod-dd-135790", + status: "In Review", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/45678901?s=16&v=4", + name: "Olivia", + }, + filesChanged: 17, + timeSaving: "6h 10m", + }, + { + task: "Linear - CDMD 1247", + pr: "codemod-dd-246801", + status: "Merged", + reviewer: { + imageUrl: "https://avatars.githubusercontent.com/u/56789012?s=16&v=4", + name: "Ethan", + }, + filesChanged: 31, + timeSaving: "11h 55m", + }, + ], +}; + +export const heroData = [ + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/78830094?s=16&v=4", + name: "Alex", + }, + codemodsCreated: 12, + prsReviewed: 14, + averageReviewTime: "2m 13s", + timeToFirstReview: "1h", + linesOfCodeDeleted: 9543, + linesOfCodeAdded: 8154, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/12345678?s=16&v=4", + name: "Emma", + }, + codemodsCreated: 8, + prsReviewed: 22, + averageReviewTime: "3m 45s", + timeToFirstReview: "45m", + linesOfCodeDeleted: 7245, + linesOfCodeAdded: 6890, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/87654321?s=16&v=4", + name: "Michael", + }, + codemodsCreated: 15, + prsReviewed: 18, + averageReviewTime: "1m 59s", + timeToFirstReview: "30m", + linesOfCodeDeleted: 12450, + linesOfCodeAdded: 11230, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/23456789?s=16&v=4", + name: "Sophia", + }, + codemodsCreated: 6, + prsReviewed: 9, + averageReviewTime: "4m 20s", + timeToFirstReview: "1h 15m", + linesOfCodeDeleted: 4320, + linesOfCodeAdded: 3980, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/34567890?s=16&v=4", + name: "Daniel", + }, + codemodsCreated: 10, + prsReviewed: 16, + averageReviewTime: "2m 50s", + timeToFirstReview: "55m", + linesOfCodeDeleted: 8765, + linesOfCodeAdded: 7890, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/45678901?s=16&v=4", + name: "Olivia", + }, + codemodsCreated: 9, + prsReviewed: 11, + averageReviewTime: "3m 10s", + timeToFirstReview: "1h 5m", + linesOfCodeDeleted: 6540, + linesOfCodeAdded: 5980, + }, + { + user: { + imageUrl: "https://avatars.githubusercontent.com/u/56789012?s=16&v=4", + name: "Ethan", + }, + codemodsCreated: 11, + prsReviewed: 20, + averageReviewTime: "2m 30s", + timeToFirstReview: "40m", + linesOfCodeDeleted: 10230, + linesOfCodeAdded: 9870, + }, +]; + +export const customData2 = [ + { + title: "useStateUpdates", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-09-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + ], + }, + { + title: "useReducerComplexity", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-09-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + ], + }, + { + title: "useContextProvider", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-09-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + ], + }, + { + title: "useRefDOMAccess", + data: [ + { + timestamp: new Date("2023-06-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-07-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-08-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + { + timestamp: new Date("2023-09-01").getTime(), + value: Math.floor(Math.random() * 30) + 5, + }, + ], + }, +]; diff --git a/apps/frontend/app/(website)/insights/[insightId]/mocks/card-revenue.json b/apps/frontend/app/(website)/insights/[insightId]/mocks/card-revenue.json new file mode 100644 index 000000000..e66dd262f --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/mocks/card-revenue.json @@ -0,0 +1,6 @@ +{ + "title": "Revenue", + "value": "$50,000", + "change": 5.2, + "subtitle": "vs last month" +} diff --git a/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-multiple.json b/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-multiple.json new file mode 100644 index 000000000..6996da789 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-multiple.json @@ -0,0 +1,146 @@ +[ + { + "name": "react-component-library", + "drift": 14.456148996899321, + "timestamp": "2023-01-14T20:00:07.000Z", + "label": "real_drift" + }, + { + "name": "react-component-library", + "drift": 35.847416442500524, + "timestamp": "2023-03-16T10:41:33.000Z", + "label": "real_drift" + }, + { + "name": "react-component-library", + "drift": 74.85711547807277, + "timestamp": "2023-05-19T21:41:57.000Z", + "label": "real_drift" + }, + { + "name": "react-component-library", + "drift": 39.26980020123616, + "timestamp": "2023-07-21T16:42:57.000Z", + "label": "real_drift" + }, + { + "name": "react-component-library", + "drift": 46.59096353792343, + "timestamp": "2023-09-26T20:31:20.000Z", + "label": "real_drift" + }, + { + "name": "react-component-library", + "drift": 65.61667932948656, + "timestamp": "2023-11-27T17:43:22.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 93.24763684401455, + "timestamp": "2023-01-29T17:46:05.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 73.27734313504043, + "timestamp": "2023-04-01T20:48:48.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 105.14931860339362, + "timestamp": "2023-06-04T17:39:17.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 130.52697865117008, + "timestamp": "2023-08-06T19:33:12.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 132.7090905357399, + "timestamp": "2023-10-08T16:10:42.000Z", + "label": "real_drift" + }, + { + "name": "react-hooks-utils", + "drift": 111.42528594016304, + "timestamp": "2023-12-12T09:43:48.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 30.127928704901542, + "timestamp": "2023-02-13T17:39:22.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 44.493726770570234, + "timestamp": "2023-04-15T01:24:25.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 32.95344873611367, + "timestamp": "2023-06-17T20:03:50.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 19.022977884556155, + "timestamp": "2023-08-21T10:17:10.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 24.48784027050521, + "timestamp": "2023-10-24T17:54:24.000Z", + "label": "real_drift" + }, + { + "name": "state-management-solution", + "drift": 28.99443520400829, + "timestamp": "2023-12-26T09:59:57.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 30.82883289869061, + "timestamp": "2023-02-27T21:39:08.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 12.334567891234567, + "timestamp": "2023-04-15T14:21:03.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 25.678934567890124, + "timestamp": "2023-06-12T19:12:45.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 18.345678901234567, + "timestamp": "2023-08-20T10:54:22.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 29.567890123456788, + "timestamp": "2023-10-15T14:30:45.000Z", + "label": "real_drift" + }, + { + "name": "react-testing-toolkit", + "drift": 40.7890123456789, + "timestamp": "2023-12-18T09:40:00.000Z", + "label": "real_drift" + } +] diff --git a/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-netlify.json b/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-netlify.json new file mode 100644 index 000000000..412bf6ab4 --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/mocks/chart-netlify.json @@ -0,0 +1,146 @@ +[ + { + "name": "netlify-react-ui", + "drift": 14.456148996899321, + "timestamp": "2016-07-14T20:00:07.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 35.847416442500524, + "timestamp": "2016-12-16T10:41:33.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 74.85711547807277, + "timestamp": "2017-05-19T21:41:57.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 39.26980020123616, + "timestamp": "2017-10-21T16:42:57.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 46.59096353792343, + "timestamp": "2018-03-26T20:31:20.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 65.61667932948656, + "timestamp": "2018-08-27T17:43:22.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 93.24763684401455, + "timestamp": "2019-01-29T17:46:05.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 73.27734313504043, + "timestamp": "2019-07-01T20:48:48.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 105.14931860339362, + "timestamp": "2019-12-04T17:39:17.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 130.52697865117008, + "timestamp": "2020-05-06T19:33:12.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 132.7090905357399, + "timestamp": "2020-10-08T16:10:42.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 11.425285940163043, + "timestamp": "2021-03-12T09:43:48.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 30.127928704901542, + "timestamp": "2021-08-13T17:39:22.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 44.493726770570234, + "timestamp": "2022-01-15T01:24:25.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 32.95344873611367, + "timestamp": "2022-06-17T20:03:50.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 19.022977884556155, + "timestamp": "2022-11-21T10:17:10.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 24.48784027050521, + "timestamp": "2023-04-24T17:54:24.000Z", + "label": "real_drift" + }, + { + "name": "netlify-react-ui", + "drift": 28.99443520400829, + "timestamp": "2023-09-26T09:59:57.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 30.82883289869061, + "timestamp": "2024-02-27T21:39:08.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 12.334567891234567, + "timestamp": "2024-03-15T14:21:03.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 25.678934567890124, + "timestamp": "2024-04-12T19:12:45.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 18.345678901234567, + "timestamp": "2024-05-20T10:54:22.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 29.567890123456788, + "timestamp": "2024-06-15T14:30:45.000Z", + "label": "real_drift" + }, + { + "name": "@netlify/source", + "drift": 40.7890123456789, + "timestamp": "2024-07-18T09:40:00.000Z", + "label": "real_drift" + } +] diff --git a/apps/frontend/app/(website)/insights/[insightId]/mocks/table-heroes.json b/apps/frontend/app/(website)/insights/[insightId]/mocks/table-heroes.json new file mode 100644 index 000000000..2a0cfcc7b --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/mocks/table-heroes.json @@ -0,0 +1,86 @@ +[ + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/78901234?s=16&v=4", + "name": "James" + }, + "codemodsCreated": 13, + "prsReviewed": 19, + "averageReviewTime": "2m 45s", + "timeToFirstReview": "50m", + "linesOfCodeDeleted": 8342, + "linesOfCodeAdded": 7591 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/89012345?s=16&v=4", + "name": "Isabella" + }, + "codemodsCreated": 7, + "prsReviewed": 25, + "averageReviewTime": "4m 10s", + "timeToFirstReview": "1h 10m", + "linesOfCodeDeleted": 6930, + "linesOfCodeAdded": 6405 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/90123456?s=16&v=4", + "name": "Benjamin" + }, + "codemodsCreated": 18, + "prsReviewed": 20, + "averageReviewTime": "2m 5s", + "timeToFirstReview": "35m", + "linesOfCodeDeleted": 13420, + "linesOfCodeAdded": 11985 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/91234567?s=16&v=4", + "name": "Mia" + }, + "codemodsCreated": 5, + "prsReviewed": 12, + "averageReviewTime": "3m 50s", + "timeToFirstReview": "1h 20m", + "linesOfCodeDeleted": 4820, + "linesOfCodeAdded": 4385 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/23456789?s=16&v=4", + "name": "Lucas" + }, + "codemodsCreated": 9, + "prsReviewed": 15, + "averageReviewTime": "2m 40s", + "timeToFirstReview": "1h", + "linesOfCodeDeleted": 7823, + "linesOfCodeAdded": 7356 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/34567890?s=16&v=4", + "name": "Charlotte" + }, + "codemodsCreated": 8, + "prsReviewed": 17, + "averageReviewTime": "3m 30s", + "timeToFirstReview": "1h 5m", + "linesOfCodeDeleted": 7030, + "linesOfCodeAdded": 6592 + }, + { + "user": { + "imageUrl": "https://avatars.githubusercontent.com/u/45678901?s=16&v=4", + "name": "Henry" + }, + "codemodsCreated": 12, + "prsReviewed": 21, + "averageReviewTime": "2m 20s", + "timeToFirstReview": "45m", + "linesOfCodeDeleted": 9834, + "linesOfCodeAdded": 9021 + } +] diff --git a/apps/frontend/app/(website)/insights/[insightId]/page.tsx b/apps/frontend/app/(website)/insights/[insightId]/page.tsx new file mode 100644 index 000000000..2110a97fa --- /dev/null +++ b/apps/frontend/app/(website)/insights/[insightId]/page.tsx @@ -0,0 +1,596 @@ +"use client"; +import RGL, { type Layout, WidthProvider } from "react-grid-layout"; +const ReactGridLayout = WidthProvider(RGL); + +import Button from "@/components/shared/Button"; +import Icon from "@/components/shared/Icon"; +import { useViewStore } from "@/store/view"; +import { cn } from "@/utils"; +import type { Widget } from "@codemod-com/database"; +import type { + ChartWidgetData, + PrimitiveWidgetData, + TableWidgetData, +} from "@codemod-com/utilities"; +import { Separator } from "@studio/components/ui/separator"; +import { get } from "lodash-es"; +import { + ArrowDown, + ArrowDownRightIcon, + ArrowUp, + GripIcon, + Loader2, +} from "lucide-react"; +import { AlignJustify, Link as LinkIcon } from "lucide-react"; +import Link from "next/link"; +import { useParams } from "next/navigation"; +import { Fragment, type ReactNode, useEffect, useMemo, useState } from "react"; +import { + Area, + CartesianGrid, + ComposedChart, + Legend, + Line, + ResponsiveContainer, + Tooltip, + type TooltipProps, + XAxis, + YAxis, +} from "recharts"; +import { v4 as uuidv4 } from "uuid"; +import { DataTable } from "../components/table/table"; +import { useInsight } from "../hooks/useInsight"; + +const SecondaryHeader = () => { + const { toggleSidebar } = useViewStore(); + + return ( +
+ + + + + All insights + + + +
+ ); +}; + +type TableWidgetProps = { + widget: Widget & { kind: "table"; data: TableWidgetData }; +}; +const TableWidget = ({ widget }: TableWidgetProps) => { + const { selectedRepos } = useViewStore(); + const insight = useInsight(widget.insightId); + + const filteredCodemodRuns = useMemo( + () => + insight.data?.codemodRuns.filter( + (run) => + run.repoUrl && selectedRepos.includes(run.repoUrl) && run.data.data, + ), + [selectedRepos, insight.data?.codemodRuns], + ); + + const usedCodemods = useMemo( + () => + widget.data.map(({ value }) => { + const matchObj = value.match(/`(\w+)\.(.+)`/); + if (matchObj?.length === 3) { + return value.match(/`(\w+)\.(.+)`/)?.at(1)!; + } + }), + [widget.data], + ); + + return ( + <> +

{widget.title}

+ + + columns={widget.data.map(({ title, value, color, icon }) => ({ + id: title, + accessorFn: (cellData, i) => { + const regexp = /`(\w+)\.(.+)`/; + const matchObj = value.match(regexp); + + // biome-ignore lint: it's ok + if (matchObj && matchObj[1] && matchObj[2]) { + const codemodRun = insight.data?.codemodRuns.find( + (run) => run.data.codemod.name === matchObj[1], + ); + + if (!codemodRun) return value; + const { data, status } = codemodRun.data; + + if (status === "done" && data && typeof data === "object") { + const replacementValue = + get(Array.isArray(data) ? data[i] : data, matchObj[2]) ?? "?"; + + return value.replace(regexp, replacementValue); + } + } + + return value; + }, + header: ({ column }) => { + const isSorted = column.getIsSorted(); + return ( +
column.toggleSorting(isSorted === "asc")} + > + {title} + {isSorted === "asc" ? ( + + ) : isSorted === "desc" ? ( + + ) : null} +
+ ); + }, + }))} + data={Array.from({ + length: Math.max( + ...(insight.data?.codemodRuns + .filter((run) => usedCodemods.includes(run.data.codemod.name)) + .map((run) => { + const resultData = run.data?.data; + + if (Array.isArray(resultData)) { + return resultData.length; + } + + return 1; + }) ?? []), + ), + })} + /> + + ); +}; + +interface TooltipPayloadItem { + name: string; + value: number; + color: string; +} + +interface CustomTooltipProps extends TooltipProps { + payload?: TooltipPayloadItem[]; +} + +const CustomTooltip: React.FC = ({ + active, + payload, + label, +}) => { + if (active && payload && payload.length) { + const uniquePayload = payload.reduce( + (acc, current) => { + if (!acc.find((item) => item.name === current.name)) { + acc.push(current); + } + return acc; + }, + [], + ); + + return ( +
+

{`${new Date(label || "").toLocaleDateString( + "en-US", + { month: "long", year: "numeric" }, + )}`}

+ {uniquePayload.map((pld, index) => ( +

{`${pld.name}: ${pld.value.toFixed(2)}`}

+ ))} +
+ ); + } + return null; +}; + +interface LegendPayloadItem { + value: string; + color: string; +} + +interface CustomLegendProps { + payload?: { value: string; color: string }[]; +} + +const CustomLegend: React.FC = ({ payload = [] }) => { + const uniquePayload = payload.reduce((acc, current) => { + return acc.some((item) => item.value === current.value) + ? acc + : acc.concat(current); + }, []); + + return ( +
    + {uniquePayload.map((entry, index) => ( +
  • + + {entry.value} +
  • + ))} +
+ ); +}; + +const colorConfig = [ + { + line: "#60A5FA", + gradientStart: "#93C5FD", + gradientEnd: "#EFF6FF", + }, + { + line: "#4ADE80", + gradientStart: "#86EFAC", + gradientEnd: "#ECFDF5", + }, +]; + +type ChartWidgetProps = { + widget: Widget & { kind: "chart"; data: ChartWidgetData }; +}; +const ChartWidget = ({ widget }: ChartWidgetProps) => { + const insight = useInsight(widget.insightId); + + const codemodRun = insight.data?.codemodRuns.find( + (run) => run.data.codemod.name === "chart_analyzer", + ); + if (!codemodRun) return null; + const { data, status } = codemodRun.data; + + const xDataKey = "timestamp"; + const yDataKeys = widget.data.y.map(({ value }) => { + const regexp = /`(\w+)\.(\w+)`/; + const matchObj = value.match(regexp); + + // biome-ignore lint: it's ok + if (matchObj && matchObj[1] && matchObj[2]) { + if (status === "done" && data) { + if (typeof data === "object") { + return matchObj[2]; + } + } + } + }); + + return ( + <> +

{widget.title}

+ + + + + + new Date(date).toLocaleDateString("en-US", { + day: "numeric", + month: "short", + year: "numeric", + }) + } + padding={{ left: 30, right: 30 }} + /> + + } /> + } /> + {widget.data.y.map((yData, index) => { + const gradientId = `gradient-${uuidv4()}`; + const color = colorConfig[index % colorConfig.length]; + + return ( + + + + + + + + + + + ); + })} + + + + ); +}; + +type PrimitiveWidgetProps = { + widget: Widget & { kind: "chart"; data: PrimitiveWidgetData }; +}; +const PrimitiveWidget = ({ widget }: PrimitiveWidgetProps) => { + const insight = useInsight(widget.insightId); + const { title } = widget; + + const formattedWidgetData: PrimitiveWidgetData = useMemo(() => { + const { ...widgetData } = Array.isArray(widget.data) + ? widget.data[0] + : widget.data; + + Object.keys(widgetData).forEach((key) => { + try { + const regexp = /`(\w+)\.(.+)`/; + const matchObj = widgetData[key].match(regexp); + + // biome-ignore lint: it's ok + if (matchObj && matchObj[1] && matchObj[2]) { + const codemodRun = insight.data?.codemodRuns.find( + (run) => run.data.codemod.name === matchObj[1], + ); + + if (!codemodRun) return; + const { data, status } = codemodRun.data; + + if (status === "done" && data) { + if (typeof data === "object") { + const replacementValue = + get(Array.isArray(data) ? data[0] : data, matchObj[2]) ?? "?"; + + widgetData[key] = widgetData[key].replace( + regexp, + replacementValue, + ); + } + } + } + } catch { + // Do nothing + } + }); + + return widgetData; + }, [insight.data?.codemodRuns, widget.data]); + + const Contents: ReactNode[] = [ +

+ {title} +

, + ]; + if (formattedWidgetData.heading) { + Contents.push( +

+ {formattedWidgetData.heading} +

, + ); + } + + if (formattedWidgetData.text) { + Contents.push( +

{formattedWidgetData.text}

, + ); + } + + if (formattedWidgetData.description) { + Contents.push( +

+ {formattedWidgetData.description} +

, + ); + } + + return
{Contents}
; +}; + +const Widget = (props: { widget: Widget }) => { + const { insightId } = useParams<{ insightId: string }>(); + const insight = useInsight(insightId); + + let Content: ReactNode; + if (props.widget.kind === "table") { + Content = ; + } else if (props.widget.kind === "chart") { + Content = ; + } else { + Content = ; + } + + const usedCodemods = useMemo(() => { + const used: (string | undefined)[] = []; + try { + if (Array.isArray(props.widget.data)) { + used.push( + ...props.widget.data.map(({ value }) => { + const matchObj = value.match(/`(\w+)\.(.+)`/); + if (matchObj?.length === 3) { + return value.match(/`(\w+)\.(.+)`/)?.at(1)!; + } + }), + ); + } + + used.push( + ...Object.keys(props.widget.data).map((key) => { + const matchObj = props.widget.data[key].match(/`(\w+)\.(.+)`/); + if (matchObj?.length === 3) { + return props.widget.data[key].match(/`(\w+)\.(.+)`/)?.at(1)!; + } + }), + ); + } catch (err) {} + + return [...new Set(used.filter(Boolean))]; + }, [props.widget.data]); + + const isLoading = useMemo( + () => + usedCodemods.some( + (c) => + insight.data?.codemodRuns.findIndex( + (run) => run.data.codemod.name === c, + ) === -1 || + insight.data?.codemodRuns.find((run) => run.data.codemod.name === c) + ?.data?.status !== "done", + ), + [usedCodemods, insight.data?.codemodRuns], + ); + + if (isLoading) { + Content = ( +
+ +
+ ); + } + + const { isResizing, isDragging } = useViewStore(); + + return ( +
+ + {Content} +
+ ); +}; + +// const availableHandles = ["s", "w", "e", "n", "sw", "nw", "se", "ne"]; + +const InsightPage = () => { + // useMirageServer(true); + + const { insightId } = useParams<{ insightId: string }>(); + const { selectedRepos, setIsDragging, setIsResizing } = useViewStore(); + const [layout, setLayout] = useState([]); + + const insightQuery = useInsight(insightId); + + useEffect(() => { + const generatedLayout: Layout[] = []; + let currentX = 0; + let currentY = 0; + + insightQuery.data?.widgets.forEach((widget, i) => { + let w: number; + let h: number; + + if (widget.kind === "table" || widget.kind === "chart") { + w = 12; + h = 9; + } else { + w = 4; + h = 4; + } + + if (currentX + w > 12) { + currentX = 0; + currentY += h; + } + + generatedLayout.push({ + x: currentX, + y: currentY, + w: w, + h: h, + i: i.toString(), + resizeHandles: ["se"], + }); + + currentX += w; + }); + + setLayout(generatedLayout); + }, [insightQuery.data?.widgets]); + + return ( +
+ + +
+ setIsResizing(true)} + onResizeStop={() => setIsResizing(false)} + onDragStart={() => setIsDragging(true)} + onDragStop={() => setIsDragging(false)} + useCSSTransforms={true} + resizeHandles={["se"]} + resizeHandle={ + + } + cols={12} + > + {insightQuery.data?.widgets.map((w, i) => ( +
+ +
+ ))} +
+
+
+ ); +}; + +export default InsightPage; diff --git a/apps/frontend/app/(website)/insights/components/AddNewInsight.tsx b/apps/frontend/app/(website)/insights/components/AddNewInsight.tsx new file mode 100644 index 000000000..a4b10c0a1 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/AddNewInsight.tsx @@ -0,0 +1,67 @@ +// @TODO move to shared or global +import Input from "@/components/shared/Input"; +import { + Dialog, + DialogContent, + DialogOverlay, + DialogPortal, + DialogTitle, +} from "@studio/components/ui/dialog"; + +import Button from "@/components/shared/Button"; +import { Folder } from "lucide-react"; +import { useState } from "react"; + +export type Props = { + open: boolean; + onAddInsight: (args: any) => void; + onOpenChange: (open: boolean) => void; +}; + +const AddNewInsightDialog = ({ onAddInsight, onOpenChange, open }: Props) => { + const [name, setName] = useState(""); + + const handleButtonClick = async () => { + onAddInsight({ name }); + onOpenChange(false); + }; + + return ( + + + + + + + Add new insight + + { + setName(e.target.value); + }} + placeholder="Insight name" + onClear={() => { + setName(""); + }} + value={name} + inputClassName="placeholder:text-secondary-light dark:placeholder:text-secondary-dark" + iconClassName="text-secondary-light dark:text-secondary-dark w-5 h-5" + commandClassName="max-h-[20px] !font-medium !body-s-medium" + className="bg-white dark:bg-black !rounded-[6px] !p-xxs" + /> +
+ +
+
+
+
+ ); +}; + +export default AddNewInsightDialog; diff --git a/apps/frontend/app/(website)/insights/components/InsightsCounter.tsx b/apps/frontend/app/(website)/insights/components/InsightsCounter.tsx new file mode 100644 index 000000000..bf91056d0 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/InsightsCounter.tsx @@ -0,0 +1,8 @@ +type Props = { + insightCount: number; +}; +const InsightsCounter = ({ insightCount }: Props) => { + return

{insightCount} Insights

; +}; + +export default InsightsCounter; diff --git a/apps/frontend/app/(website)/insights/components/InsightsTable.tsx b/apps/frontend/app/(website)/insights/components/InsightsTable.tsx new file mode 100644 index 000000000..1c4758563 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/InsightsTable.tsx @@ -0,0 +1,65 @@ +"use client"; +import { Table } from "@radix-ui/themes"; + +import Button from "@/components/shared/Button"; +import { useViewStore } from "@/store/view"; +import { MoreVertical, Star } from "lucide-react"; +import { useRouter } from "next/navigation"; +import { memo, useMemo } from "react"; +import { useInsights } from "../hooks/useInsights"; + +const InsightsTable = () => { + const { data: insightsData } = useInsights(); + const { insightsSearchTerm } = useViewStore(); + + const filteredInsights = useMemo( + () => + insightsData?.data.filter((insight) => + insight.name?.includes(insightsSearchTerm), + ), + [insightsSearchTerm, insightsData], + ); + + const { push } = useRouter(); + + return ( + + + + Name + Updated On + Owner +   + + + + + {filteredInsights?.map((insight) => ( + push(`/insights/${insight.id}`)} + > + {insight.name} + {insight.updatedAt} + {insight.ownerId} + {/* + + {insight.owner.name} + */} + + + + + + ))} + + + ); +}; + +export default memo(InsightsTable); diff --git a/apps/frontend/app/(website)/insights/components/RepoList.tsx b/apps/frontend/app/(website)/insights/components/RepoList.tsx new file mode 100644 index 000000000..4b860f8d9 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/RepoList.tsx @@ -0,0 +1,31 @@ +import type { GithubRepository } from "@codemod-com/api-types"; +import { memo } from "react"; +import RepositoryItem from "./RepositoryItem"; + +type Props = { + repositories: GithubRepository[]; + selectedRepositoryId: GithubRepository["id"] | null; + onRepoSelect(repo: GithubRepository): void; +}; + +const RepoList = ({ + repositories, + selectedRepositoryId, + onRepoSelect, +}: Props) => { + return ( +
    + {repositories.map((repository) => ( +
  • + +
  • + ))} +
+ ); +}; + +export default memo(RepoList); diff --git a/apps/frontend/app/(website)/insights/components/RepositoryItem.tsx b/apps/frontend/app/(website)/insights/components/RepositoryItem.tsx new file mode 100644 index 000000000..6dab6c164 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/RepositoryItem.tsx @@ -0,0 +1,32 @@ +import type { GithubRepository } from "@codemod-com/api-types"; +import { cx } from "cva"; +import RepositoryLanguage from "./RepositoryLanguage"; + +type Props = { + repository: GithubRepository; + isActive: boolean; + onClick(repo: GithubRepository): void; +}; + +const RepositoryItem = ({ repository, isActive, onClick }: Props) => { + return ( +
onClick(repository)} + className={cx( + "flex flex-col gap-[12px] py-xs px-[12px] bg-emphasis-light dark:bg-emphasis-dark rounded-[8px] hover:bg-primary-dark dark:hover:bg-primary-light border cursor-pointer", + { + // @TODO make List and ListItem components, that handles hover, active logic + "!bg-primary-dark !dark:bg-primary-light border-2": isActive, + }, + )} + > + {repository.name} + {repository.language && ( + + )} +
+ ); +}; + +export default RepositoryItem; diff --git a/apps/frontend/app/(website)/insights/components/RepositoryLanguage.tsx b/apps/frontend/app/(website)/insights/components/RepositoryLanguage.tsx new file mode 100644 index 000000000..9fc369e8c --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/RepositoryLanguage.tsx @@ -0,0 +1,216 @@ +const languageColorMap = { + ABAP: "#E8274B", + ActionScript: "#882B0F", + Ada: "#02f88c", + Agda: "#315665", + "AGS Script": "#B9D9FF", + Alloy: "#64C800", + AMPL: "#E6EFBB", + ANTLR: "#9DC3FF", + "API Blueprint": "#2ACCA8", + APL: "#5A8164", + Arc: "#aa2afe", + Arduino: "#bd79d1", + ASP: "#6a40fd", + AspectJ: "#a957b0", + Assembly: "#6E4C13", + ATS: "#1ac620", + AutoHotkey: "#6594b9", + AutoIt: "#1C3552", + BlitzMax: "#cd6400", + Boo: "#d4bec1", + Brainfuck: "#2F2530", + "C Sharp": "#178600", + C: "#555555", + Chapel: "#8dc63f", + Cirru: "#ccccff", + Clarion: "#db901e", + Clean: "#3F85AF", + Click: "#E4E6F3", + Clojure: "#db5855", + CoffeeScript: "#244776", + "ColdFusion CFC": "#ed2cd6", + ColdFusion: "#ed2cd6", + "Common Lisp": "#3fb68b", + "Component Pascal": "#b0ce4e", + cpp: "#f34b7d", + Crystal: "#776791", + CSS: "#563d7c", + D: "#ba595e", + Dart: "#00B4AB", + Diff: "#88dddd", + DM: "#447265", + Dogescript: "#cca760", + Dylan: "#6c616e", + E: "#ccce35", + Eagle: "#814C05", + eC: "#913960", + ECL: "#8a1267", + edn: "#db5855", + Eiffel: "#946d57", + Elixir: "#6e4a7e", + Elm: "#60B5CC", + "Emacs Lisp": "#c065db", + EmberScript: "#FFF4F3", + Erlang: "#B83998", + "F#": "#b845fc", + Factor: "#636746", + Fancy: "#7b9db4", + Fantom: "#dbded5", + FLUX: "#88ccff", + Forth: "#341708", + FORTRAN: "#4d41b1", + FreeMarker: "#0050b2", + Frege: "#00cafe", + "Game Maker Language": "#8fb200", + Glyph: "#e4cc98", + Gnuplot: "#f0a9f0", + Go: "#375eab", + Golo: "#88562A", + Gosu: "#82937f", + "Grammatical Framework": "#79aa7a", + Groovy: "#e69f56", + Handlebars: "#01a9d6", + Harbour: "#0e60e3", + Haskell: "#29b544", + Haxe: "#df7900", + HTML: "#e44b23", + Hy: "#7790B2", + IDL: "#a3522f", + Io: "#a9188d", + Ioke: "#078193", + Isabelle: "#FEFE00", + J: "#9EEDFF", + Java: "#b07219", + JavaScript: "#f1e05a", + JFlex: "#DBCA00", + JSONiq: "#40d47e", + Julia: "#a270ba", + "Jupyter Notebook": "#DA5B0B", + Kotlin: "#F18E33", + KRL: "#28431f", + Lasso: "#999999", + Latte: "#A8FF97", + Lex: "#DBCA00", + LFE: "#004200", + LiveScript: "#499886", + LOLCODE: "#cc9900", + LookML: "#652B81", + LSL: "#3d9970", + Lua: "#000080", + Makefile: "#427819", + Mask: "#f97732", + Matlab: "#bb92ac", + Max: "#c4a79c", + MAXScript: "#00a6a6", + Mercury: "#ff2b2b", + Metal: "#8f14e9", + Mirah: "#c7a938", + MTML: "#b7e1f4", + NCL: "#28431f", + Nemerle: "#3d3c6e", + nesC: "#94B0C7", + NetLinx: "#0aa0ff", + "NetLinx+ERB": "#747faa", + NetLogo: "#ff6375", + NewLisp: "#87AED7", + Nimrod: "#37775b", + Nit: "#009917", + Nix: "#7e7eff", + Nu: "#c9df40", + "Objective-C": "#438eff", + "Objective-C++": "#6866fb", + "Objective-J": "#ff0c5a", + OCaml: "#3be133", + Omgrofl: "#cabbff", + ooc: "#b0b77e", + Opal: "#f7ede0", + Oxygene: "#cdd0e3", + Oz: "#fab738", + Pan: "#cc0000", + Papyrus: "#6600cc", + Parrot: "#f3ca0a", + Pascal: "#b0ce4e", + PAWN: "#dbb284", + Perl: "#0298c3", + Perl6: "#0000fb", + PHP: "#4F5D95", + PigLatin: "#fcd7de", + Pike: "#005390", + PLSQL: "#dad8d8", + PogoScript: "#d80074", + Processing: "#0096D8", + Prolog: "#74283c", + "Propeller Spin": "#7fa2a7", + Puppet: "#302B6D", + "Pure Data": "#91de79", + PureBasic: "#5a6986", + PureScript: "#1D222D", + Python: "#3572A5", + QML: "#44a51c", + R: "#198ce7", + Racket: "#22228f", + "Ragel in Ruby Host": "#9d5200", + RAML: "#77d9fb", + Rebol: "#358a5b", + Red: "#ee0000", + "Ren'Py": "#ff7f7f", + Rouge: "#cc0088", + Ruby: "#701516", + Rust: "#dea584", + SaltStack: "#646464", + SAS: "#B34936", + Scala: "#DC322F", + Scheme: "#1e4aec", + Self: "#0579aa", + Shell: "#89e051", + Shen: "#120F14", + Slash: "#007eff", + Slim: "#ff8f77", + Smalltalk: "#596706", + SourcePawn: "#5c7611", + SQF: "#3F3F3F", + Squirrel: "#800000", + Stan: "#b2011d", + "Standard ML": "#dc566d", + SuperCollider: "#46390b", + Swift: "#ffac45", + SystemVerilog: "#DAE1C2", + Tcl: "#e4cc98", + TeX: "#3D6117", + Turing: "#45f715", + TypeScript: "#2b7489", + "Unified Parallel C": "#4e3617", + "Unity3D Asset": "#ab69a1", + UnrealScript: "#a54c4d", + Vala: "#fbe5cd", + Verilog: "#b2b7f8", + VHDL: "#adb2cb", + VimL: "#199f4b", + "Visual Basic": "#945db7", + Volt: "#1F1F1F", + Vue: "#2c3e50", + "Web Ontology Language": "#9cc9dd", + wisp: "#7582D1", + X10: "#4B6BEF", + xBase: "#403a40", + XC: "#99DA07", + XQuery: "#5232e7", + Zephir: "#118f9e", +}; + +type Language = keyof typeof languageColorMap; + +const RepositoryLanguage = ({ language }: { language: Language }) => { + return ( + + + {language} + + ); +}; + +export default RepositoryLanguage; diff --git a/apps/frontend/app/(website)/insights/components/RepositorySelector.tsx b/apps/frontend/app/(website)/insights/components/RepositorySelector.tsx new file mode 100644 index 000000000..6cb4c535e --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/RepositorySelector.tsx @@ -0,0 +1,103 @@ +import Input from "@/components/shared/Input"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogOverlay, + DialogPortal, + DialogTitle, +} from "@studio/components/ui/dialog"; + +import Button from "@/components/shared/Button"; +import type { GithubRepository } from "@codemod-com/api-types"; +import useDebounce from "@studio/hooks/useDebounce"; +import { Folder } from "lucide-react"; +import { useMemo, useState } from "react"; +import { useRepositories } from "../hooks/useRepositories"; +import RepoList from "./RepoList"; + +export type Props = { + open: boolean; + onConfirm: (repo: string) => void; + onOpenChange: (open: boolean) => void; +}; + +const RepositorySelector = ({ onConfirm, onOpenChange, open }: Props) => { + const { data, isLoading } = useRepositories(); + + const [search, setSearch] = useState(""); + const debouncedSearch = useDebounce(search, 300); + + const [selectedRepository, setSelectedRepository] = + useState(); + + const handleButtonClick = async () => { + if (!selectedRepository) { + return; + } + + onConfirm(selectedRepository.html_url); + }; + + const filteredRepos = useMemo(() => { + return (data?.data ?? []).filter((repo) => + repo.name.includes(debouncedSearch), + ); + }, [debouncedSearch, data?.data]); + + return ( + + + + + + + Select repository + + + This repository will be analyzed to provide all the insights and + recommended actions + + { + setSearch(e.target.value); + }} + placeholder="Search" + icon={"search"} + onClear={() => { + setSearch(""); + }} + value={search} + inputClassName="placeholder:text-secondary-light dark:placeholder:text-secondary-dark" + iconClassName="text-secondary-light dark:text-secondary-dark w-5 h-5" + commandClassName="max-h-[20px] !font-medium !body-s-medium" + className="bg-white dark:bg-black !rounded-[6px] !p-xxs" + /> + {isLoading && "Loading..."} + {filteredRepos.length !== 0 ? ( +
+ +
+ ) : ( +

No repositories found

+ )} +
+ +
+
+
+
+ ); +}; + +export default RepositorySelector; diff --git a/apps/frontend/app/(website)/insights/components/SearchBox.tsx b/apps/frontend/app/(website)/insights/components/SearchBox.tsx new file mode 100644 index 000000000..5b8b8e08b --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/SearchBox.tsx @@ -0,0 +1,68 @@ +"use client"; +import Input from "@/components/shared/Input"; +import { useRouter } from "next/navigation"; +import React, { useEffect, useState, type ChangeEvent } from "react"; + +type Props = { + placeholder?: string; + onSearch: (value: string) => void; +}; + +const SearchBox = ({ placeholder, onSearch }: Props) => { + const inputWrapperRef = React.useRef(null); + const router = useRouter(); + + useEffect(() => { + function handleFocus(event: KeyboardEvent) { + const input = inputWrapperRef.current?.querySelector("input"); + if (event.metaKey && event.key === "k") { + event.preventDefault(); + input?.focus(); + } + } + + window.addEventListener("keydown", handleFocus); + return () => { + window.removeEventListener("keydown", handleFocus); + }; + }, [router]); + + const [searchInput, setSearchInput] = useState(""); + + const handleChange = (e: ChangeEvent) => { + const nextValue = e.target.value; + setSearchInput(nextValue); + + onSearch(nextValue); + }; + + return ( +
+
{ + const input = inputWrapperRef.current?.querySelector("input"); + input?.focus(); + }} + className="w-full" + > + { + setSearchInput(""); + }} + value={searchInput} + inputClassName="placeholder:text-secondary-light dark:placeholder:text-secondary-dark" + iconClassName="text-secondary-light dark:text-secondary-dark w-5 h-5" + commandClassName="max-h-[20px] !font-medium !body-s-medium" + className="bg-white dark:bg-black !rounded-[6px] !p-xxs" + /> +
+
+ ); +}; + +export default SearchBox; diff --git a/apps/frontend/app/(website)/insights/components/SecondaryHeader.tsx b/apps/frontend/app/(website)/insights/components/SecondaryHeader.tsx new file mode 100644 index 000000000..497e51559 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/SecondaryHeader.tsx @@ -0,0 +1,84 @@ +"use client"; +import Button from "@/components/shared/Button"; +import Icon from "@/components/shared/Icon"; +import { useViewStore } from "@/store/view"; +import { Separator } from "@studio/components/ui/separator"; +import { AlignJustify, Folder, Plus, Settings } from "lucide-react"; +import { useInsights } from "../hooks/useInsights"; +import InsightsCounter from "./InsightsCounter"; +import SearchBox from "./SearchBox"; + +type Props = { + selectedRepoName: string; + onOpenRepoSelector(): void; + onAddNewInsight(): void; +}; + +const SecondaryHeader = ({ + selectedRepoName, + onOpenRepoSelector, + onAddNewInsight, +}: Props) => { + const { data: insightsData } = useInsights(); + const { toggleSidebar, setInsightsSearchTerm } = useViewStore(); + + return ( +
+ + + + + + + {/* Buttons padding is different then in other place @TODO check why */} + + + + +
+ ); +}; + +export default SecondaryHeader; diff --git a/apps/frontend/app/(website)/insights/components/table/scroll.tsx b/apps/frontend/app/(website)/insights/components/table/scroll.tsx new file mode 100644 index 000000000..cc26c2e6d --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/table/scroll.tsx @@ -0,0 +1,71 @@ +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; +import * as React from "react"; + +import { cn } from "@/utils"; + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + withHorizontal?: boolean; + forceMount?: true; + zIndex?: number; + } +>( + ( + { className, children, withHorizontal, forceMount, zIndex, ...props }, + ref, + ) => ( + + + {children} + + + {withHorizontal && ( + + )} + + + ), +); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef< + typeof ScrollAreaPrimitive.ScrollAreaScrollbar + > & { + zIndex?: number; + } +>(({ className, orientation = "vertical", zIndex, ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export { ScrollArea, ScrollBar }; diff --git a/apps/frontend/app/(website)/insights/components/table/table-shadcn.tsx b/apps/frontend/app/(website)/insights/components/table/table-shadcn.tsx new file mode 100644 index 000000000..575a1caf2 --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/table/table-shadcn.tsx @@ -0,0 +1,113 @@ +import { cn } from "@/utils"; +import * as React from "react"; + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + //
+ + // +)); +Table.displayName = "Table"; + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableHeader.displayName = "TableHeader"; + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableBody.displayName = "TableBody"; + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableFooter.displayName = "TableFooter"; + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableRow.displayName = "TableRow"; + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableHead.displayName = "TableHead"; + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableCell.displayName = "TableCell"; + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableCaption.displayName = "TableCaption"; + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +}; diff --git a/apps/frontend/app/(website)/insights/components/table/table.tsx b/apps/frontend/app/(website)/insights/components/table/table.tsx new file mode 100644 index 000000000..536d5038b --- /dev/null +++ b/apps/frontend/app/(website)/insights/components/table/table.tsx @@ -0,0 +1,112 @@ +"use client"; +import { + type ColumnDef, + type SortingState, + type TableOptions, + flexRender, + getCoreRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { useState } from "react"; +import { ScrollArea } from "./scroll"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "./table-shadcn"; + +export interface DataTableProps { + data: T[]; + columns: ColumnDef[]; + sortingState?: SortingState; + setSortingState?: React.Dispatch>; +} + +export const DataTable = ({ + data, + columns, + // sortingState, + // setSortingState, +}: DataTableProps) => { + const [sorting, setSorting] = useState([]); + + const options: TableOptions = { + data, + columns, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + onSortingChange: setSorting, + state: { + sorting, + }, + }; + + // possible back-end sorting + // if (setSortingState) { + // options.manualSorting = true; + // options.onSortingChange = setSortingState; + // options.state!.sorting = sortingState; + // } + + const table = useReactTable(options); + if (!table) return null; + + return ( + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ ); +}; diff --git a/apps/frontend/app/(website)/insights/hooks/useCreateInsightMutation.ts b/apps/frontend/app/(website)/insights/hooks/useCreateInsightMutation.ts new file mode 100644 index 000000000..ceadb7af3 --- /dev/null +++ b/apps/frontend/app/(website)/insights/hooks/useCreateInsightMutation.ts @@ -0,0 +1,18 @@ +import { CREATE_INSIGHT } from "@/mocks/endpoints/insights"; +import type { Insight } from "@codemod-com/database"; +import { useMutation, useQueryClient } from "react-query"; +import { useAPI } from "../../studio/src/hooks/useAPI"; + +export const useCreateInsightMutation = () => { + const queryClient = useQueryClient(); + + const { post: createInsight } = useAPI(CREATE_INSIGHT); + return useMutation({ + mutationFn: createInsight, + onSuccess: (data) => { + queryClient.setQueryData(["insights"], (old) => { + return [...(old ?? []), data.data]; + }); + }, + }); +}; diff --git a/apps/frontend/app/(website)/insights/hooks/useInitiateInsightsMutation.ts b/apps/frontend/app/(website)/insights/hooks/useInitiateInsightsMutation.ts new file mode 100644 index 000000000..1e7dbdc37 --- /dev/null +++ b/apps/frontend/app/(website)/insights/hooks/useInitiateInsightsMutation.ts @@ -0,0 +1,18 @@ +import { CREATE_INSIGHT } from "@/mocks/endpoints/insights"; +import type { Insight } from "@codemod-com/database"; +import { useMutation, useQueryClient } from "react-query"; +import { useAPI } from "../../studio/src/hooks/useAPI"; + +export const useInitiateInsightsMutation = () => { + const queryClient = useQueryClient(); + + const { post: createInsight } = useAPI(CREATE_INSIGHT); + return useMutation({ + mutationFn: createInsight, + onSuccess: (data) => { + queryClient.setQueryData(["insights"], (old) => { + return [...(old ?? []), data.data]; + }); + }, + }); +}; diff --git a/apps/frontend/app/(website)/insights/hooks/useInsight.ts b/apps/frontend/app/(website)/insights/hooks/useInsight.ts new file mode 100644 index 000000000..fc1293f64 --- /dev/null +++ b/apps/frontend/app/(website)/insights/hooks/useInsight.ts @@ -0,0 +1,47 @@ +import type { CodemodRun, Insight, Widget } from "@codemod-com/database"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useQuery } from "react-query"; +import * as v from "valibot"; + +export const useInsight = (insightId: string | number) => { + const { get: getInsight } = useAPI< + Insight & { + widgets: Widget[]; + codemodRuns: Pick[]; + } + >(`/insights/${insightId}`); + + return useQuery(["insight"], async () => { + const res = await getInsight(); + + return { + ...res.data, + codemodRuns: res.data.codemodRuns.map((run) => { + if (!run.data.data) { + return run; + } + + try { + const parsedData = v.parse( + v.union([ + v.array(v.record(v.string(), v.unknown())), + v.record(v.string(), v.unknown()), + ]), + JSON.parse(run.data.data || "{}"), + ); + + return { ...run, data: { ...run.data, data: parsedData } }; + } catch (err) { + if (typeof run.data.data === "string") { + return { + ...run, + data: { ...run.data, data: { message: run.data.data } }, + }; + } + } + + return run; + }), + }; + }); +}; diff --git a/apps/frontend/app/(website)/insights/hooks/useInsights.ts b/apps/frontend/app/(website)/insights/hooks/useInsights.ts new file mode 100644 index 000000000..747233314 --- /dev/null +++ b/apps/frontend/app/(website)/insights/hooks/useInsights.ts @@ -0,0 +1,16 @@ +import { GET_ALL_INSIGHTS } from "@/mocks/endpoints/insights"; +import type { Insight } from "@codemod-com/database"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useQuery } from "react-query"; + +export const useInsights = () => { + const { get: getInsights } = useAPI<{ data: Insight[]; total: number }>( + GET_ALL_INSIGHTS, + ); + + return useQuery(["insights"], async () => { + const res = await getInsights(); + + return res.data; + }); +}; diff --git a/apps/frontend/app/(website)/insights/hooks/useRepositories.tsx b/apps/frontend/app/(website)/insights/hooks/useRepositories.tsx new file mode 100644 index 000000000..f475f7c09 --- /dev/null +++ b/apps/frontend/app/(website)/insights/hooks/useRepositories.tsx @@ -0,0 +1,10 @@ +import type { GithubRepository } from "@codemod-com/api-types"; +import { GH_REPO_LIST } from "@mocks/endpoints/gh-run"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useQuery } from "react-query"; + +export const useRepositories = () => { + const { get: getRepositoriesAPI } = useAPI(GH_REPO_LIST); + + return useQuery(["repos"], getRepositoriesAPI); +}; diff --git a/apps/frontend/app/(website)/insights/layout.tsx b/apps/frontend/app/(website)/insights/layout.tsx new file mode 100644 index 000000000..39b5448a1 --- /dev/null +++ b/apps/frontend/app/(website)/insights/layout.tsx @@ -0,0 +1,160 @@ +"use client"; +import ThemeSwitcher from "@/components/global/Footer/ThemeSwitcher"; +import Logo from "@/components/shared/Logo"; +import { useTheme } from "@/hooks/useTheme"; +import { useViewStore } from "@/store/view"; +import { Theme } from "@radix-ui/themes"; +import "@radix-ui/themes/styles.css"; +import NavLink from "@/components/shared/NavLink"; +import { + AreaChartIcon, + BookOpen, + HistoryIcon, + TerminalIcon, + TextSearch, +} from "lucide-react"; +import { usePathname } from "next/navigation"; +import { useMemo } from "react"; +import { QueryClient, QueryClientProvider } from "react-query"; + +const client = new QueryClient(); + +const useSidebarNav = () => { + const sidebarNav = useMemo( + () => ({ + topNavLinks: [ + { + label: "Studio", + href: "/studio", + Icon: TerminalIcon, + }, + { + label: "Registry", + href: "/registry", + Icon: TextSearch, + }, + { + label: "Insights", + href: "/insights", + Icon: AreaChartIcon, + }, + { + label: "Runs", + href: "/runs", + Icon: HistoryIcon, + }, + ], + }), + [], + ); + + return sidebarNav; +}; + +const Layout = ({ children }: { children: React.ReactNode }) => { + const { isSidebarActive, setIsSidebarActive } = useViewStore(); + + const { topNavLinks } = useSidebarNav(); + const { toggleTheme, theme } = useTheme(); + const pathname = usePathname(); + + return ( + + +
+
+ {/* Mobile sidebar */} + {/*
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+
*/} + + {/* Static sidebar for desktop */} +
+ {/* Sidebar component, swap this element with another sidebar if you like */} +
+
+ +
+ +
+
+ +
+
{children}
+
+
+
+
+
+ ); +}; + +export default Layout; diff --git a/apps/frontend/app/(website)/insights/page.tsx b/apps/frontend/app/(website)/insights/page.tsx new file mode 100644 index 000000000..c376576dc --- /dev/null +++ b/apps/frontend/app/(website)/insights/page.tsx @@ -0,0 +1,127 @@ +"use client"; + +import Tabs, { TabContent } from "@/components/shared/Tabs"; +import { useMemo, useState } from "react"; + +import AddNewInsightDialog from "./components/AddNewInsight"; +import InsightsTable from "./components/InsightsTable"; +import RepositorySelector from "./components/RepositorySelector"; +import SecondaryHeader from "./components/SecondaryHeader"; + +import { useAuth } from "@/app/auth/useAuth"; +import Button from "@/components/shared/Button"; +import { useViewStore } from "@/store/view"; +import { ChartBar, GithubLogo } from "@phosphor-icons/react"; +import { Separator } from "@radix-ui/react-separator"; +import Link from "next/link"; +import { Card } from "../studio/src/components/ui/card"; +import { useCreateInsightMutation } from "./hooks/useCreateInsightMutation"; +import { useInsights } from "./hooks/useInsights"; + +export type InsightsTabsConfig = [ + { id: "my"; label: "All" }, + { id: "all_available"; label: "Recommended" }, + { id: "featured"; label: "Featured" }, +]; + +const InsightsPage = () => { + const tabsConfig = useMemo( + () => + [ + { id: "my", label: "All" }, + { id: "all_available", label: "Recommended" }, + { id: "featured", label: "Featured" }, + ] satisfies InsightsTabsConfig, + [], + ); + const insightsQuery = useInsights(); + + const { selectedRepos, setSelectedRepos } = useViewStore(); + const [repoSelectorOpen, setRepoSelectorOpen] = useState(false); + const [addNewInsightModalOpen, setAddNewInsightModalOpen] = useState(false); + + const { mutateAsync } = useCreateInsightMutation(); + + const { isSignedIn } = useAuth(); + if (!isSignedIn) { + return ( + +
+
+ +

Codemod Insights

+
+

+ Build or modify auto-generated dashboards to manage and track + external, internal, and incremental migrations across your entire + stack, all powered by codemods. +

+ + + +

Connect your GitHub to get started.

+ + + +
+ +
+ Insights Preview +
+
+ ); + } + + return ( + <> +
+ setRepoSelectorOpen(true)} + onAddNewInsight={() => setAddNewInsightModalOpen(true)} + selectedRepoName={selectedRepos.join(",") ?? "Select Repository"} + /> +
+ + {tabsConfig.map(({ id, label }) => ( + + + + ))} + +
+
+ + { + setSelectedRepos([repo]); + setRepoSelectorOpen(false); + }} + onOpenChange={setRepoSelectorOpen} + /> + + + ); +}; + +export default InsightsPage; diff --git a/apps/frontend/app/(website)/layout.tsx b/apps/frontend/app/(website)/layout.tsx index 9f1a28540..c2e55a95c 100644 --- a/apps/frontend/app/(website)/layout.tsx +++ b/apps/frontend/app/(website)/layout.tsx @@ -9,6 +9,7 @@ import { loadGlobalData } from "@/data/sanity"; import { GLOBAL_QUERY } from "@/data/sanity/queries"; import { getOgImages } from "@/data/sanity/resolveSanityRouteMetadata"; import { mergeDeepRight } from "ramda"; +import AuthProvider from "../context/AuthProvider"; import { metadata } from "./studio/studioMetadata"; const LiveVisualEditing = dynamic( @@ -71,7 +72,9 @@ export default async function Layout({ {children} ) : ( - {children} + + {children} + ))} {draftMode().isEnabled && } diff --git a/apps/frontend/app/(website)/runs/hooks/useCodemodRuns.ts b/apps/frontend/app/(website)/runs/hooks/useCodemodRuns.ts new file mode 100644 index 000000000..3f4f9e7c8 --- /dev/null +++ b/apps/frontend/app/(website)/runs/hooks/useCodemodRuns.ts @@ -0,0 +1,16 @@ +import { GET_ALL_CODEMOD_RUNS } from "@/mocks/endpoints/insights"; +import type { CodemodRun } from "@codemod-com/database"; +import { useAPI } from "@studio/hooks/useAPI"; +import { useQuery } from "react-query"; + +export const useCodemodRuns = () => { + const { get: getInsights } = useAPI<{ data: CodemodRun[]; total: number }>( + GET_ALL_CODEMOD_RUNS, + ); + + return useQuery(["codemod-runs"], async () => { + const res = await getInsights(); + + return res.data; + }); +}; diff --git a/apps/frontend/app/(website)/runs/layout.tsx b/apps/frontend/app/(website)/runs/layout.tsx new file mode 100644 index 000000000..ed823e004 --- /dev/null +++ b/apps/frontend/app/(website)/runs/layout.tsx @@ -0,0 +1,160 @@ +"use client"; +import ThemeSwitcher from "@/components/global/Footer/ThemeSwitcher"; +import Logo from "@/components/shared/Logo"; +import NavLink from "@/components/shared/NavLink"; +import { useTheme } from "@/hooks/useTheme"; +import { useViewStore } from "@/store/view"; +import { Theme } from "@radix-ui/themes"; +import "@radix-ui/themes/styles.css"; +import { + AreaChartIcon, + BookOpen, + HistoryIcon, + TerminalIcon, + TextSearch, +} from "lucide-react"; +import { usePathname } from "next/navigation"; +import { useMemo } from "react"; +import { QueryClient, QueryClientProvider } from "react-query"; + +const client = new QueryClient(); + +const useSidebarNav = () => { + const sidebarNav = useMemo( + () => ({ + topNavLinks: [ + { + label: "Studio", + href: "/studio", + Icon: TerminalIcon, + }, + { + label: "Registry", + href: "/registry", + Icon: TextSearch, + }, + { + label: "Insights", + href: "/insights", + Icon: AreaChartIcon, + }, + { + label: "Runs", + href: "/runs", + Icon: HistoryIcon, + }, + ], + }), + [], + ); + + return sidebarNav; +}; + +const Layout = ({ children }: { children: React.ReactNode }) => { + const { isSidebarActive, setIsSidebarActive } = useViewStore(); + + const { topNavLinks } = useSidebarNav(); + const { toggleTheme, theme } = useTheme(); + const pathname = usePathname(); + + return ( + + +
+
+ {/* Mobile sidebar */} + {/*
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+
*/} + + {/* Static sidebar for desktop */} +
+ {/* Sidebar component, swap this element with another sidebar if you like */} +
+
+ +
+ +
+
+ +
+
{children}
+
+
+
+
+
+ ); +}; + +export default Layout; diff --git a/apps/frontend/app/(website)/runs/page.tsx b/apps/frontend/app/(website)/runs/page.tsx new file mode 100644 index 000000000..31b3e8b48 --- /dev/null +++ b/apps/frontend/app/(website)/runs/page.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { useAuth } from "@/app/auth/useAuth"; +import Button from "@/components/shared/Button"; +import { ChartBar, GithubLogo } from "@phosphor-icons/react"; +import { Separator } from "@radix-ui/react-separator"; +import Link from "next/link"; +import { DataTable } from "../insights/components/table/table"; +import { Card } from "../studio/src/components/ui/card"; +import { useCodemodRuns } from "./hooks/useCodemodRuns"; + +const InsightsPage = () => { + const { data: codemodRuns } = useCodemodRuns(); + + const { isSignedIn } = useAuth(); + if (!isSignedIn) { + return ( + +
+
+ +

Codemod Insights

+
+

+ Build or modify auto-generated dashboards to manage and track + external, internal, and incremental migrations across your entire + stack, all powered by codemods. +

+ + + +

Connect your GitHub to get started.

+ + + +
+ +
+ Insights Preview +
+
+ ); + } + + return ( + JSON.stringify(row.data), + }, + { accessorKey: "createdAt", header: "Created At" }, + ]} + /> + ); +}; + +export default InsightsPage; diff --git a/apps/frontend/app/(website)/studio/features/GHRun/components/RepositoryModal.tsx b/apps/frontend/app/(website)/studio/features/GHRun/components/RepositoryModal.tsx index 2691d491d..106bbd29c 100644 --- a/apps/frontend/app/(website)/studio/features/GHRun/components/RepositoryModal.tsx +++ b/apps/frontend/app/(website)/studio/features/GHRun/components/RepositoryModal.tsx @@ -1,5 +1,5 @@ import type { - CodemodRunRequest, + CodemodRunRequestPayload, GHBranch, GithubRepository, } from "@codemod-com/api-types"; @@ -14,7 +14,7 @@ import { isNil } from "ramda"; import { useState } from "react"; export type RepositoryModalProps = { - onCodemodRun: (request: CodemodRunRequest) => Promise; + onCodemodRun: (request: CodemodRunRequestPayload) => Promise; hideRepositoryModal: VoidFunction; isRepositoryModalShown: boolean; repositoriesToShow: GithubRepository[]; diff --git a/apps/frontend/app/(website)/studio/features/GHRun/hooks/useCodemodExecution.ts b/apps/frontend/app/(website)/studio/features/GHRun/hooks/useCodemodExecution.ts index a82012ad2..309c1191c 100644 --- a/apps/frontend/app/(website)/studio/features/GHRun/hooks/useCodemodExecution.ts +++ b/apps/frontend/app/(website)/studio/features/GHRun/hooks/useCodemodExecution.ts @@ -1,7 +1,4 @@ -import type { - CodemodRunRequest, - CodemodRunStatus, -} from "@codemod-com/api-types"; +import type { CodemodRunRequestPayload } from "@codemod-com/api-types"; import { RUN_CODEMOD as RUN_CODEMOD_URL } from "@mocks/endpoints/gh-run"; import { useAPI } from "@studio/hooks/useAPI"; import type { ToVoid } from "@studio/types/transformations"; @@ -13,13 +10,17 @@ export const useCodemodExecution = ({ codemodExecutionId: string | null; setCodemodExecutionId: ToVoid; }) => { - const { post: runCodemod } = useAPI(RUN_CODEMOD_URL); + const { post: runCodemod } = useAPI<{ success: boolean; ids: string[] }>( + RUN_CODEMOD_URL, + ); - const onCodemodRun = async (request: CodemodRunRequest): Promise => { + const onCodemodRun = async ( + request: CodemodRunRequestPayload, + ): Promise => { try { - const { codemodRunId, success } = (await runCodemod(request)).data; + const { ids, success } = (await runCodemod(request)).data; if (!success) return; - setCodemodExecutionId(codemodRunId); + setCodemodExecutionId(ids[0] ?? null); } catch (e) { console.error(e); } diff --git a/apps/frontend/app/(website)/studio/features/GHRun/hooks/useHandleCodemodRun.ts b/apps/frontend/app/(website)/studio/features/GHRun/hooks/useHandleCodemodRun.ts index b4e87fb94..edb334316 100644 --- a/apps/frontend/app/(website)/studio/features/GHRun/hooks/useHandleCodemodRun.ts +++ b/apps/frontend/app/(website)/studio/features/GHRun/hooks/useHandleCodemodRun.ts @@ -1,5 +1,5 @@ import type { - CodemodRunRequest, + CodemodRunRequestPayload, GHBranch, GithubRepository, } from "@codemod-com/api-types"; @@ -8,7 +8,7 @@ import { useSnippetsStore } from "@studio/store/snippets"; import { transpileTs } from "@studio/utils/transpileTs"; type Props = { - onCodemodRun: (request: CodemodRunRequest) => Promise; + onCodemodRun: (request: CodemodRunRequestPayload) => Promise; selectedRepository: GithubRepository | undefined; selectedBranch: GHBranch | undefined; }; @@ -32,7 +32,8 @@ export const useHandleCodemodRun = ({ return; } - const request = { + const request: CodemodRunRequestPayload & { codemodSource: string } = { + codemodName: "studio-generated", codemodEngine: engine, repoUrl: selectedRepository.html_url, codemodSource: (await transpileTs(content)).transpiled, diff --git a/apps/frontend/app/(website)/studio/page.tsx b/apps/frontend/app/(website)/studio/page.tsx index 12d590be3..ae5a20650 100644 --- a/apps/frontend/app/(website)/studio/page.tsx +++ b/apps/frontend/app/(website)/studio/page.tsx @@ -1,5 +1,4 @@ "use client"; -import AuthProvider from "@/app/context/AuthProvider"; import { isServer } from "@studio/config"; import { MainPage } from "@studio/main/index"; import { Suspense, useEffect } from "react"; @@ -17,18 +16,16 @@ export default function Page() { return ( - -
- - -
- -
+
+ + +
+
); } diff --git a/apps/frontend/app/(website)/studio/src/hooks/useAPI.ts b/apps/frontend/app/(website)/studio/src/hooks/useAPI.ts index 7e5f8b36a..0eab01cfe 100644 --- a/apps/frontend/app/(website)/studio/src/hooks/useAPI.ts +++ b/apps/frontend/app/(website)/studio/src/hooks/useAPI.ts @@ -8,7 +8,7 @@ const shouldUseMocks = process.env.NODE_ENV === "development" && Boolean(localStorage?.getItem("useMocks")); -export const useAPI = (endpoint: string) => { +export const useAPI = (endpoint: string) => { useMirageServer(shouldUseMocks); const { getToken } = useAuth(); @@ -23,11 +23,10 @@ export const useAPI = (endpoint: string) => { }; return { - get: async () => - await apiClient.get(endpoint, await getHeaders()), - put: async (body: U) => - await apiClient.put(endpoint, body, await getHeaders()), - post: async (body: U) => - await apiClient.post(endpoint, body, await getHeaders()), + get: async () => await apiClient.get(endpoint, await getHeaders()), + put: async (body: U) => + await apiClient.put(endpoint, body, await getHeaders()), + post: async (body: U) => + await apiClient.post(endpoint, body, await getHeaders()), }; }; diff --git a/apps/frontend/components/global/GlobalLayout.tsx b/apps/frontend/components/global/GlobalLayout.tsx index 65f98aece..d68af1cd2 100644 --- a/apps/frontend/components/global/GlobalLayout.tsx +++ b/apps/frontend/components/global/GlobalLayout.tsx @@ -3,8 +3,10 @@ import { useHideMenu } from "@/components/global/useHideMenu"; import type { GlobalPagePayload } from "@/types"; import { cn } from "@/utils"; +import { usePathname } from "next/navigation"; import Footer from "./Footer"; import Navigation from "./Navigation"; +import PlatformHeader from "./PlatformHeader"; export default function GlobalLayout({ data, @@ -16,6 +18,11 @@ export default function GlobalLayout({ children: any; }) { const hideMenu = useHideMenu(); + const pathname = usePathname(); + + const isPlatformPage = + pathname.startsWith("/insights") || pathname.startsWith("/runs"); + return (
- {!hideMenu && data && } -
+ {/* @TODO refactor this logic */} + {isPlatformPage ? ( + + ) : ( + !hideMenu && data && + )} + {} + +
{children}
{!hideMenu && data &&
} diff --git a/apps/frontend/components/global/Navigation/DesktopNavigation.tsx b/apps/frontend/components/global/Navigation/DesktopNavigation.tsx index 2fe1c49ff..8a62b77c3 100644 --- a/apps/frontend/components/global/Navigation/DesktopNavigation.tsx +++ b/apps/frontend/components/global/Navigation/DesktopNavigation.tsx @@ -1,6 +1,5 @@ "use client"; -import AuthProvider from "@/app/context/AuthProvider"; import { GithubPermissions } from "@/components/GithubPermissions"; import { TokenBuilder } from "@/components/TokenBuilder"; import Button from "@/components/shared/Button"; @@ -143,11 +142,11 @@ export function DesktopNavigationRight(props: { ))} {shouldRenderAuth && ( - + <> - + )}
); diff --git a/apps/frontend/components/global/PlatformHeader/index.tsx b/apps/frontend/components/global/PlatformHeader/index.tsx new file mode 100644 index 000000000..8eb5a9930 --- /dev/null +++ b/apps/frontend/components/global/PlatformHeader/index.tsx @@ -0,0 +1,16 @@ +import AuthButtons from "@/app/auth/AuthButtons"; +import { usePathname } from "next/navigation"; +import LogoWithContextMenu from "../Navigation/LogoWithContextMenu"; + +const PlatformHeader = () => { + const pathname = usePathname(); + + return ( +
+ + +
+ ); +}; + +export default PlatformHeader; diff --git a/apps/frontend/components/global/useHideMenu.ts b/apps/frontend/components/global/useHideMenu.ts index b9039a3d3..a1f8d1bd5 100644 --- a/apps/frontend/components/global/useHideMenu.ts +++ b/apps/frontend/components/global/useHideMenu.ts @@ -2,6 +2,6 @@ import { usePathname } from "next/navigation"; export const useHideMenu = () => { const pathname = usePathname(); - const menuLessRoutes = ["/studio", "/sign-in"]; + const menuLessRoutes = ["/studio", "/sign-in", "/insights"]; return menuLessRoutes.some((route) => pathname.includes(route)); }; diff --git a/apps/frontend/components/shared/Button.tsx b/apps/frontend/components/shared/Button.tsx index 18f51a352..c808a58fa 100644 --- a/apps/frontend/components/shared/Button.tsx +++ b/apps/frontend/components/shared/Button.tsx @@ -18,6 +18,7 @@ export type Button = { icon?: IconName | React.ReactElement; iconPosition?: "left" | "right"; loading?: boolean; + loadingOpacity?: boolean; glow?: boolean; flush?: boolean; }; @@ -29,6 +30,7 @@ export type ButtonWithArrow = { icon?: never; iconPosition?: never; loading?: boolean; + loadingOpacity?: boolean; glow?: boolean; flush?: boolean; }; @@ -40,6 +42,7 @@ export type ButtonWithIconOnly = { icon: IconName; iconPosition?: never; loading?: boolean; + loadingOpacity?: boolean; glow?: boolean; flush?: boolean; }; @@ -106,6 +109,7 @@ const Button = forwardRef( icon, iconPosition, loading = false, + loadingOpacity = true, children, ...props }, @@ -136,9 +140,13 @@ const Button = forwardRef( {typeof icon === "string" && iconPosition && iconPosition === "left" ? ( ) : null} - - {children} - + {loadingOpacity ? ( + + {children} + + ) : ( + children + )} {loading ? ( diff --git a/apps/frontend/components/shared/Input.tsx b/apps/frontend/components/shared/Input.tsx index b717b991c..a19379b38 100644 --- a/apps/frontend/components/shared/Input.tsx +++ b/apps/frontend/components/shared/Input.tsx @@ -15,6 +15,7 @@ type InputProps = { onClear?: () => void; inputClassName?: string; iconClassName?: string; + commandClassName?: string; } & ( | React.InputHTMLAttributes | React.InputHTMLAttributes @@ -108,7 +109,12 @@ export default function Input({ /> )} {command ? ( - + ) : null} diff --git a/apps/frontend/components/shared/NavLink.tsx b/apps/frontend/components/shared/NavLink.tsx new file mode 100644 index 000000000..60aff389b --- /dev/null +++ b/apps/frontend/components/shared/NavLink.tsx @@ -0,0 +1,38 @@ +import { cva, cx } from "cva"; +import Link, { type LinkProps } from "next/link"; + +// @TODO move to global components +type Props = LinkProps & { + children: React.ReactNode; + intent: "default" | "active"; +}; + +const navLinkVariant = cva( + [ + "body-s-medium flex grow items-center gap-xs rounded-[4px] focus:outline-none px-[6px] py-[4px] font-medium text-primary-light dark:text-primary-dark", + "transition-colors", + ], + { + variants: { + intent: { + default: [ + "bg-primary-dark dark:bg-primary-light hover:bg-emphasis-light dark:hover:bg-emphasis-dark", + ], + active: [ + "bg-emphasis-light dark:bg-emphasis-dark hover:bg-border-light dark:hover:bg-border-dark", + ], + }, + }, + defaultVariants: { + intent: "default", + }, + }, +); + +const NavLink = ({ children, href, intent }: Props) => ( + + {children} + +); + +export default NavLink; diff --git a/apps/frontend/components/shared/Tabs.tsx b/apps/frontend/components/shared/Tabs.tsx index 9418e89e6..4a7b8ee2b 100644 --- a/apps/frontend/components/shared/Tabs.tsx +++ b/apps/frontend/components/shared/Tabs.tsx @@ -1,26 +1,34 @@ "use client"; +import { cn } from "@/utils"; import * as RadixTabs from "@radix-ui/react-tabs"; import { cx } from "cva"; import { motion } from "framer-motion"; import { useState } from "react"; -type TabsProps = { +export type TabsProps = { items: { id: string; label: string; }[]; children: React.ReactNode; listClassName?: string; + itemClassName?: string; }; -export default function Tabs({ items, children, listClassName }: TabsProps) { +export default function Tabs({ + items, + children, + listClassName, + itemClassName, +}: TabsProps) { const [activeTab, setActiveTab] = useState(items[0].id); return ( setActiveTab(newValue as string)} + className="w-full" > diff --git a/apps/frontend/components/templates/Registry/CodemodList.tsx b/apps/frontend/components/templates/Registry/CodemodList.tsx index 7e3083e70..6db020c02 100644 --- a/apps/frontend/components/templates/Registry/CodemodList.tsx +++ b/apps/frontend/components/templates/Registry/CodemodList.tsx @@ -29,6 +29,7 @@ export default function CodemodList({ total: initial?.total || 0, }); + // debugger; const { ref, inView } = useInView({ threshold: 1, }); diff --git a/apps/frontend/env.ts b/apps/frontend/env.ts index 095126fd3..3eb6d6782 100644 --- a/apps/frontend/env.ts +++ b/apps/frontend/env.ts @@ -21,6 +21,7 @@ export const env = createEnv({ HUBSPOT_CONTACT_FORM_ID: z.string(), HUBSPOT_NEWSLETTER_FORM_ID: z.string(), IS_PREVIEW: z.string().optional(), + CLERK_SECRET_KEY: z.string(), }, client: { NEXT_PUBLIC_API_URL: z.string(), @@ -64,5 +65,6 @@ export const env = createEnv({ HUBSPOT_CONTACT_FORM_ID: process.env.HUBSPOT_CONTACT_FORM_ID, HUBSPOT_NEWSLETTER_FORM_ID: process.env.HUBSPOT_NEWSLETTER_FORM_ID, IS_PREVIEW: process.env.IS_PREVIEW, + CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY, }, }); diff --git a/apps/frontend/middleware.ts b/apps/frontend/middleware.ts index 8241efe3e..549086771 100644 --- a/apps/frontend/middleware.ts +++ b/apps/frontend/middleware.ts @@ -1,18 +1,16 @@ import { getRedirect } from "@/data/sanity/redirects"; -// @TODO @codemod-com/utilities imports node runtime libs, cannot be used in midddleware -// @TODO modular import @codemod-com/utilities/constants -// import { -// CODEMOD_STUDIO_URL, -// OLD_STUDIO_HOSTNAME, -// } from "@codemod-com/utilities"; +import { env } from "@/env"; const CODEMOD_STUDIO_URL = "https://codemod.com/studio"; const OLD_STUDIO_HOSTNAME = "codemod.studio"; import { type NextRequest, NextResponse } from "next/server"; -// @TODO: Handle redirects from Sanity -export async function middleware(request: NextRequest) { +import { clerkMiddleware } from "@clerk/nextjs/server"; + +const isProtectedRoute = () => false; + +async function middleware(request: NextRequest) { if (request.nextUrl.hostname === OLD_STUDIO_HOSTNAME) { return NextResponse.redirect(new URL(CODEMOD_STUDIO_URL)); } @@ -96,6 +94,20 @@ export async function middleware(request: NextRequest) { } } +export default clerkMiddleware( + (auth, req) => { + if (isProtectedRoute(req)) { + auth().protect(); + } + + return middleware(req); + }, + { + publishableKey: env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, + secretKey: env.CLERK_SECRET_KEY, + }, +); + export const config = { matcher: [ "/((?!_next/static|_next/image|favicon.ico|manage|blocks|favicons|fonts|images|studio-docs).*)", diff --git a/apps/frontend/mirage/endpoints/codemodRun.ts b/apps/frontend/mirage/endpoints/codemodRun.ts index 960dbc7cd..71e671e4b 100644 --- a/apps/frontend/mirage/endpoints/codemodRun.ts +++ b/apps/frontend/mirage/endpoints/codemodRun.ts @@ -3,9 +3,17 @@ import { RUN_CODEMOD } from "../../mocks/endpoints/gh-run"; import type { AppRegistry } from ".."; +let currentId = 0; + export const codemodRunEndpoints = (server: Server) => { - server.post( - RUN_CODEMOD, - () => new Response(200, {}, { codemodRunId: "1", success: true }), - ); + server.post(RUN_CODEMOD, () => { + currentId++; + return new Response( + 200, + {}, + { + ids: [{ id: currentId.toString(), workflow: "drift_analyzer" }], + }, + ); + }); }; diff --git a/apps/frontend/mirage/endpoints/executionStatus.ts b/apps/frontend/mirage/endpoints/executionStatus.ts index 0b9a49c60..8d706c142 100644 --- a/apps/frontend/mirage/endpoints/executionStatus.ts +++ b/apps/frontend/mirage/endpoints/executionStatus.ts @@ -1,7 +1,6 @@ import type { Result } from "@codemod-com/api-types"; -import { Response, type Server } from "miragejs"; +import type { Server } from "miragejs"; import type { AppRegistry } from ".."; -import { EXECUTION_STATUS } from "../../mocks/endpoints/gh-run"; const isSuccess = true; const errorExecutionResult: Result = { @@ -9,34 +8,192 @@ const errorExecutionResult: Result = { message: "error msg", }; +const result = [ + { + name: "netlify-react-ui", + drift: 0, + timestamp: "2016-02-13T06:39:36.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 35.35185527423561, + timestamp: "2016-12-12T08:21:10.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 77.52109899587259, + timestamp: "2017-06-15T13:49:49.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 34.399063635803614, + timestamp: "2017-09-15T23:46:10.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 43.8585323449489, + timestamp: "2017-12-01T23:17:14.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 48.25834890517943, + timestamp: "2018-04-19T22:06:04.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 113.00711171345066, + timestamp: "2019-05-09T16:56:45.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 142.19867622196207, + timestamp: "2020-07-23T07:26:12.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 10.07275987871072, + timestamp: "2021-02-10T20:54:49.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 27.866417517129033, + timestamp: "2021-07-23T10:14:44.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 47.68886424772583, + timestamp: "2022-02-02T17:05:28.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 32.95344873611367, + timestamp: "2022-06-16T16:36:37.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 18.97643346543735, + timestamp: "2022-10-20T16:16:02.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 26.168915172796158, + timestamp: "2023-02-01T19:14:06.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 22.48369234138962, + timestamp: "2023-03-31T15:52:10.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 25.098393533063646, + timestamp: "2023-06-08T16:05:31.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 27.756901236849487, + timestamp: "2023-08-18T14:54:48.000Z", + label: "real_drift", + }, + { + name: "netlify-react-ui", + drift: 39.07814671074697, + timestamp: "2023-10-31T18:42:55.000Z", + label: "real_drift", + }, + { + name: "@netlify/source", + drift: 31.031438017207748, + timestamp: "2024-02-07T12:17:32.000Z", + label: "real_drift", + }, + { + name: "@netlify/source", + drift: 30.037577773670915, + timestamp: "2024-04-24T12:47:15.000Z", + label: "real_drift", + }, + { + name: "@netlify/source", + drift: 40.258184630759025, + timestamp: "2024-08-13T14:15:00.000Z", + label: "real_drift", + }, +]; + const buildGetResponse = () => { const responses = [ - { - status: "progress", - message: "progress msg 1", - }, - { - status: "progress", - message: "progress msg 2", - }, - { - status: "executing codemod", - progress: { processed: 0, total: 100 }, - }, - { - status: "executing codemod", - progress: { processed: 30, total: 100 }, - }, - { - status: "executing codemod", - progress: { processed: 80, total: 100 }, - }, - isSuccess - ? { - status: "done", - link: "https://www.google.com", // PR Link - } - : errorExecutionResult, + [ + { + status: "queued", + message: "Codemod queued", + id: "1", + codemod: "drift_analyzer", + progress: 0, + }, + ], + [ + { + status: "in_progress", + message: "Processing codemod", + id: "2", + codemod: "drift_analyzer", + progress: 20, + }, + ], + [ + { + status: "in_progress", + message: "Processing codemod", + id: "3", + codemod: "drift_analyzer", + progress: 40, + }, + ], + [ + { + status: "in_progress", + message: "Processing codemod", + id: "4", + codemod: "drift_analyzer", + progress: 70, + }, + ], + [ + { + status: "in_progress", + message: "Processing codemod", + id: "5", + codemod: "drift_analyzer", + progress: 90, + }, + ], + [ + { + status: "success", + message: "Successfully processed", + id: "6", + codemod: "drift_analyzer", + progress: 100, + result: JSON.stringify(result), + }, + ], ]; let index = 0; @@ -44,25 +201,22 @@ const buildGetResponse = () => { return () => { const currentResponse = responses[index]; - index++; + if (index < responses.length - 1) { + index++; + } else { + index = 0; + } return currentResponse; }; }; -const responsesMap = new Map Result>(); +const getResponse = buildGetResponse(); export const executionStatusEndpoints = (server: Server) => { - server.get(EXECUTION_STATUS, (_, request) => { - const codemodRunId = request.params.id ?? ""; - - if (!responsesMap.has(codemodRunId)) { - responsesMap.set(codemodRunId, buildGetResponse()); - } - - const getResponse = responsesMap.get(codemodRunId)!; + server.get("/run/codemodRun/status", () => { const nextResponse = getResponse(); - return new Response(200, {}, { result: nextResponse, success: true }); + return nextResponse; }); }; diff --git a/apps/frontend/mirage/endpoints/index.ts b/apps/frontend/mirage/endpoints/index.ts index 173939e55..138815b63 100644 --- a/apps/frontend/mirage/endpoints/index.ts +++ b/apps/frontend/mirage/endpoints/index.ts @@ -1,4 +1,4 @@ -export * from "./campaign"; +export * from "./insight"; export * from "./repository"; export * from "./branch"; export * from "./codemodRun"; diff --git a/apps/frontend/mirage/endpoints/insight.ts b/apps/frontend/mirage/endpoints/insight.ts new file mode 100644 index 000000000..8e6efbeec --- /dev/null +++ b/apps/frontend/mirage/endpoints/insight.ts @@ -0,0 +1,11 @@ +import type { Server } from "miragejs"; +import type { AppRegistry } from ".."; + +export const insightEndpoints = (server: Server) => { + server.get("/insights/", (schema) => schema.all("insight").models); + server.post("/insight", (schema, request) => { + const body = JSON.parse(request.requestBody); + + return schema.create("insight", body).attrs; + }); +}; diff --git a/apps/frontend/mirage/factories/index.ts b/apps/frontend/mirage/factories/index.ts index 990468793..78b42bcae 100644 --- a/apps/frontend/mirage/factories/index.ts +++ b/apps/frontend/mirage/factories/index.ts @@ -1,4 +1,4 @@ -export * from "./campaign"; +export * from "./insight"; export * from "./dashboard"; export * from "./repository"; export * from "./branch"; diff --git a/apps/frontend/mirage/factories/insight.ts b/apps/frontend/mirage/factories/insight.ts new file mode 100644 index 000000000..87eea047a --- /dev/null +++ b/apps/frontend/mirage/factories/insight.ts @@ -0,0 +1,8 @@ +import { faker } from "@faker-js/faker"; +import { Factory } from "miragejs"; + +export const insightFactory = Factory.extend({ + name() { + return faker.company.name(); + }, +}); diff --git a/apps/frontend/mirage/index.ts b/apps/frontend/mirage/index.ts index e6c48f453..00a728e18 100644 --- a/apps/frontend/mirage/index.ts +++ b/apps/frontend/mirage/index.ts @@ -4,33 +4,33 @@ import type Schema from "miragejs/orm/schema"; import { branchEndpoints, - campaignEndpoints, codemodRunEndpoints, executionStatusEndpoints, + insightEndpoints, repositoryEndpoints, } from "./endpoints"; import { branchFactory, - campaignFactory, dashboardFactory, + insightFactory, repositoryFactory, } from "./factories"; import { branchModel, - campaignModel, dashboardModel, + insightModel, repositoryModel, } from "./models"; -import { createBranches, createCampaigns, createRepositories } from "./seeds"; +import { createBranches, createInsights, createRepositories } from "./seeds"; const models = { - campaign: campaignModel, + insight: insightModel, dashboard: dashboardModel, repository: repositoryModel, branch: branchModel, }; const factories = { - campaign: campaignFactory, + insight: insightFactory, dashboard: dashboardFactory, repository: repositoryFactory, branch: branchFactory, @@ -49,7 +49,7 @@ export const runServer = (environment: string) => this.urlPrefix = env.NEXT_PUBLIC_API_URL; this.timing = 250; - campaignEndpoints(this); + insightEndpoints(this); repositoryEndpoints(this); branchEndpoints(this); codemodRunEndpoints(this); @@ -84,7 +84,7 @@ export const runServer = (environment: string) => this.passthrough("http://localhost:3000/_next/**"); }, seeds(server) { - createCampaigns(server); + createInsights(server); createBranches(server); createRepositories(server); }, diff --git a/apps/frontend/mirage/models/dashboard.ts b/apps/frontend/mirage/models/dashboard.ts index c8c856bec..a68cc6704 100644 --- a/apps/frontend/mirage/models/dashboard.ts +++ b/apps/frontend/mirage/models/dashboard.ts @@ -1,5 +1,5 @@ import { Model, belongsTo } from "miragejs"; export const dashboardModel = Model.extend({ - campaign: belongsTo(), + insight: belongsTo(), }); diff --git a/apps/frontend/mirage/models/index.ts b/apps/frontend/mirage/models/index.ts index 990468793..78b42bcae 100644 --- a/apps/frontend/mirage/models/index.ts +++ b/apps/frontend/mirage/models/index.ts @@ -1,4 +1,4 @@ -export * from "./campaign"; +export * from "./insight"; export * from "./dashboard"; export * from "./repository"; export * from "./branch"; diff --git a/apps/frontend/mirage/models/insight.ts b/apps/frontend/mirage/models/insight.ts new file mode 100644 index 000000000..1e98f2732 --- /dev/null +++ b/apps/frontend/mirage/models/insight.ts @@ -0,0 +1,5 @@ +import { Model, hasMany } from "miragejs"; + +export const insightModel = Model.extend({ + dashboard: hasMany(), +}); diff --git a/apps/frontend/mirage/seeds/index.ts b/apps/frontend/mirage/seeds/index.ts index 3a95d4209..4a4d08ad3 100644 --- a/apps/frontend/mirage/seeds/index.ts +++ b/apps/frontend/mirage/seeds/index.ts @@ -1,3 +1,3 @@ -export * from "./campaign"; +export * from "./insight"; export * from "./branch"; export * from "./repository"; diff --git a/apps/frontend/mirage/seeds/insight.ts b/apps/frontend/mirage/seeds/insight.ts new file mode 100644 index 000000000..b4765991e --- /dev/null +++ b/apps/frontend/mirage/seeds/insight.ts @@ -0,0 +1,13 @@ +import type { Server } from "miragejs"; +import type { AppRegistry } from ".."; + +export const createInsights = (server: Server) => { + server.create("insight", { + name: "[Auto generated] Project dependency freshness", + owner: "Alex", + }); + server.create("insight", { + name: "React 17 to 18 Migration", + owner: "Alex", + }); +}; diff --git a/apps/frontend/mocks/endpoints/gh-run.ts b/apps/frontend/mocks/endpoints/gh-run.ts index 1e35a9e2c..cff1070ff 100644 --- a/apps/frontend/mocks/endpoints/gh-run.ts +++ b/apps/frontend/mocks/endpoints/gh-run.ts @@ -1,15 +1,29 @@ const GH_REPO_LIST = "/sourceControl/github/user/repos"; const GH_BRANCH_LIST = "/sourceControl/github/repo/branches"; const RUN_CODEMOD = "/run/codemodRun"; -const EXECUTION_STATUS = `/run/codemodRun/status/:id`; -const GET_EXECUTION_STATUS = (jobId: string) => - `/run/codemodRun/status/${jobId}`; +const GET_EXECUTION_STATUS = (jobIds: string[]) => + `/run/codemodRun/status?ids=${jobIds.join(",")}`; + +const buildGetWorkflowRunUrl = (workflowRunId: string) => + `/workflow/runs/${workflowRunId}`; + +const buildCreateWorkflowRunUrl = (workflowId: string) => + `workflow/${workflowId}/run`; + +const buildCancelWorkflowRunUrl = (runId: string) => + `workflow/runs/${runId}/cancel`; + +const getWorkflowRunArtifactsUrl = (runId: string) => + `workflow/runs/${runId}/artifacts`; export { - GH_REPO_LIST, + GET_EXECUTION_STATUS, GH_BRANCH_LIST, + GH_REPO_LIST, RUN_CODEMOD, - EXECUTION_STATUS, - GET_EXECUTION_STATUS, + buildGetWorkflowRunUrl, + buildCreateWorkflowRunUrl, + buildCancelWorkflowRunUrl, + getWorkflowRunArtifactsUrl, }; diff --git a/apps/frontend/mocks/endpoints/insights.ts b/apps/frontend/mocks/endpoints/insights.ts new file mode 100644 index 000000000..467924a54 --- /dev/null +++ b/apps/frontend/mocks/endpoints/insights.ts @@ -0,0 +1,4 @@ +export const GET_ALL_CODEMOD_RUNS = "/codemod-runs"; +export const GET_ALL_INSIGHTS = "/insights"; +export const CREATE_INSIGHT = "/insights"; +export const INITIATE_INSIGHTS = "/insights/new"; diff --git a/apps/frontend/mocks/handlers/workflow-runs.ts b/apps/frontend/mocks/handlers/workflow-runs.ts new file mode 100644 index 000000000..15d8e5df3 --- /dev/null +++ b/apps/frontend/mocks/handlers/workflow-runs.ts @@ -0,0 +1,139 @@ +import { + buildCreateWorkflowRunUrl, + buildGetWorkflowRunUrl, + getWorkflowRunArtifactsUrl, +} from "../endpoints/gh-run"; + +import type { CodemodRunStatus, Result } from "@codemod-com/api-types"; + +const isSuccess = true; +const errorExecutionResult: Result = { + status: "error", + message: "error msg", +}; +let workflowRunResultsIndex = 0; + +const getWorkflowRun = () => [ + { + state: "queued", + message: "Executing workflow step: Clone repo", + progress: 0, + }, + { + state: "in_progress", + message: "Executing workflow step: Running analyzer", + progress: 0, + }, + { + state: "in_progress", + progress: 20, + message: "Executing workflow step: Running analyzer", + }, + { + state: "in_progress", + progress: 40, + message: "Executing workflow step: Running analyzer", + }, + { + state: "in_progress", + progress: 90, + message: "Executing workflow step: Running analyzer", + }, + { + state: "in_progress", + progress: 95, + message: "Executing workflow step: Running analyzer", + }, + { + state: "in_progress", + progress: 99, + message: "Executing workflow step: Generating report", + }, + { + state: "done", + artifactsUrl: "https://codemod.com/workflow/runs/1/artifacts", + }, +]; + +export const mockedWorkflowRunEndpoints = { + [buildCreateWorkflowRunUrl("1")]: { + post: (): { data: CodemodRunStatus } => ({ + data: { workflowRunId: "1", success: true }, + }), + }, + [buildGetWorkflowRunUrl("1")]: { + get: () => { + const index = workflowRunResultsIndex; + workflowRunResultsIndex = + workflowRunResultsIndex === getWorkflowRun().length + ? 0 + : workflowRunResultsIndex + 1; + return { + data: getWorkflowRun()[index] || null, + }; + }, + }, + [getWorkflowRunArtifactsUrl("1")]: { + get: () => { + return { + data: [ + { + repo: { + name: "reponame", + }, + migrations: [ + { + id: "1", + name: "Nextjs Pages to App migration", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "recommended", + }, + { + id: "2", + name: "Platform Eng Team - Code Health", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "recommended", + }, + { + id: "3", + name: "React Components to Vue.js Transition Overview", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "recommended", + }, + { + id: "4", + name: "Nextjs Pages to App migration", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "recommended", + }, + { + id: "5", + name: "Nextjs Pages to App migration", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "featured", + }, + { + id: "6", + name: "Nextjs Pages to App migration", + category: "Codemod", + updatedOn: "27 Jul, 2:30 PM", + owner: "Alex", + type: "featured", + }, + ], + }, + ], + }; + }, + }, +}; diff --git a/apps/frontend/next.config.mjs b/apps/frontend/next.config.mjs index 373e3d8e3..b4583528a 100644 --- a/apps/frontend/next.config.mjs +++ b/apps/frontend/next.config.mjs @@ -2,6 +2,8 @@ import MonacoEditorPlugin from "monaco-editor-webpack-plugin"; /** @type {import('next').NextConfig} */ const config = { + reactStrictMode: true, + transpilePackages: ["recharts"], webpack: (config, { isServer, webpack }) => { if (!isServer) { config.plugins.push( diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 75af2b0ba..7dd11cabc 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -38,9 +38,10 @@ "@clerk/nextjs": "catalog:", "@clerk/themes": "catalog:", "@codemod-com/api-types": "workspace:*", + "@codemod-com/database": "workspace:*", "@codemod-com/utilities": "workspace:*", - "@faker-js/faker": "^8.4.1", "@codemod.com/codemod-utils": "workspace:*", + "@faker-js/faker": "^8.4.1", "@hookform/resolvers": "catalog:", "@monaco-editor/react": "^4.4.6", "@phosphor-icons/react": "catalog:", @@ -59,6 +60,7 @@ "@radix-ui/react-tabs": "catalog:", "@radix-ui/react-toast": "catalog:", "@radix-ui/react-tooltip": "catalog:", + "@radix-ui/themes": "^3.1.1", "@sanity/client": "^6.21.0", "@sanity/code-input": "catalog:", "@sanity/orderable-document-list": "catalog:", @@ -68,7 +70,7 @@ "@sanity/vision": "catalog:", "@swc/wasm-web": "^1.6.7", "@t3-oss/env-nextjs": "catalog:", - "@tailwindcss/typography": "catalog:", + "@tanstack/react-table": "^8.20.1", "@tinloof/sanity-studio": "catalog:", "@tinloof/sanity-web": "catalog:", "@types/jscodeshift": "^0.11.6", @@ -114,9 +116,11 @@ "react-arborist": "^3.4.0", "react-chartjs-2": "catalog:", "react-dom": "18.2.0", + "react-grid-layout": "1.3.4", "react-hook-form": "catalog:", "react-hot-toast": "catalog:", "react-markdown": "catalog:", + "react-query": "^3.39.3", "react-resizable-panels": "^2.0.15", "react-syntax-highlighter": "catalog:", "react-textarea-autosize": "catalog:", @@ -126,6 +130,7 @@ "react-use": "catalog:", "reactjs-popup": "catalog:", "recast": "catalog:", + "recharts": "^2.12.7", "rehype-sanitize": "catalog:", "remark-gfm": "catalog:", "remark-math": "catalog:", @@ -155,6 +160,7 @@ "devDependencies": { "@babel/core": "^7.24.7", "@babel/preset-env": "^7.1.6", + "@tailwindcss/typography": "catalog:", "@testing-library/dom": "catalog:", "@testing-library/jest-dom": "catalog:", "@testing-library/react": "catalog:", @@ -168,6 +174,7 @@ "@types/prettier": "catalog:", "@types/react": "18.2.55", "@types/react-dom": "catalog:", + "@types/react-grid-layout": "^1.3.5", "@types/react-syntax-highlighter": "catalog:", "@types/react-treeview": "catalog:", "@types/react-virtualized-auto-sizer": "catalog:", diff --git a/apps/frontend/public/insights-preview.png b/apps/frontend/public/insights-preview.png new file mode 100644 index 000000000..e656c3f68 Binary files /dev/null and b/apps/frontend/public/insights-preview.png differ diff --git a/apps/frontend/store/view.ts b/apps/frontend/store/view.ts new file mode 100644 index 000000000..47659457a --- /dev/null +++ b/apps/frontend/store/view.ts @@ -0,0 +1,32 @@ +import { create } from "zustand"; + +type ViewState = { + isResizing: boolean; + setIsResizing: (isResizing: boolean) => void; + isDragging: boolean; + setIsDragging: (isDragging: boolean) => void; + isSidebarActive: boolean; + setIsSidebarActive: (isSidebarActive: boolean) => void; + insightsSearchTerm: string; + selectedRepos: string[]; + toggleSidebar: () => void; + setInsightsSearchTerm: (searchTerm: string) => void; + setSelectedRepos: (repos: string[]) => void; +}; + +export const useViewStore = create((set) => ({ + isSidebarActive: true, + setIsSidebarActive: (isSidebarActive: boolean) => + set(() => ({ isSidebarActive })), + isDragging: false, + setIsDragging: (isDragging: boolean) => set(() => ({ isDragging })), + isResizing: false, + setIsResizing: (isResizing: boolean) => set(() => ({ isResizing })), + insightsSearchTerm: "", + selectedRepos: [], + toggleSidebar: () => + set(({ isSidebarActive }) => ({ isSidebarActive: !isSidebarActive })), + setInsightsSearchTerm: (searchTerm: string) => + set(() => ({ insightsSearchTerm: searchTerm })), + setSelectedRepos: (repos: string[]) => set(() => ({ selectedRepos: repos })), +})); diff --git a/apps/frontend/styles/globals.css b/apps/frontend/styles/globals.css index d7564a30a..98126eb39 100644 --- a/apps/frontend/styles/globals.css +++ b/apps/frontend/styles/globals.css @@ -10,6 +10,7 @@ --text-color-code-plain: #0b151e; --text-color-code-comments: #0b151e66; --header-height: 114px; + --platform-header-height: 52px; } :root.dark { --text-color-code-plain: #fff; @@ -362,4 +363,42 @@ h6[class*="heading"] a { .tab-item.input-error { border-color: red; -} \ No newline at end of file +} + +/* React Grid Layout (react-grid-layout) */ +.react-grid-layout { + position: relative; + transition: height 200ms ease; +} +.react-grid-item { + transition: all 200ms ease; + transition-property: left, top; +} +.react-grid-item.cssTransforms { + transition-property: transform; +} +.react-grid-item.resizing { + z-index: 1; +} + +.react-grid-item.react-draggable-dragging { + transition: none; + z-index: 3; +} + +.react-grid-item.react-grid-placeholder { + background: #000; + opacity: 0.1; + transition-duration: 100ms; + z-index: 2; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.dark .react-grid-item.react-grid-placeholder { + background: #FFF; +} diff --git a/apps/frontend/tailwind.config.ts b/apps/frontend/tailwind.config.ts index d93c04fc1..43425688e 100644 --- a/apps/frontend/tailwind.config.ts +++ b/apps/frontend/tailwind.config.ts @@ -111,7 +111,7 @@ const colorPalette = { light: "#FEA80026", dark: "#FEA80033", }, -}; +} as const; const colorsT = { accent: "#D6FF62", @@ -175,7 +175,7 @@ const colorsT = { light: "#FEA80026", dark: "#FEA80033", }, -}; +} as const; /** @type {import('tailwindcss').Config} */ export default { darkMode: "selector", diff --git a/apps/run-service/.env.example b/apps/run-service/.env.example index e7b24f396..b8cb29c45 100644 --- a/apps/run-service/.env.example +++ b/apps/run-service/.env.example @@ -1,7 +1,8 @@ +NODE_ENV= + PORT= -AWS_SECRET_ACCESS_KEY= -AWS_ACCESS_KEY_ID= +AUTH_SERVICE_URL= DATABASE_URI= diff --git a/apps/run-service/Dockerfile b/apps/run-service/Dockerfile index df8bcf17c..5c1404d71 100644 --- a/apps/run-service/Dockerfile +++ b/apps/run-service/Dockerfile @@ -1,34 +1,60 @@ -FROM node:20-alpine3.16 as builder +FROM node:20-alpine3.16 AS base -WORKDIR /app +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" -RUN npm install -g pnpm +RUN npm install -g pnpm@latest +RUN pnpm -g add turbo@^1 -COPY ./package.json ./turbo.json ./pnpm-workspace.yaml ./pnpm-lock.yaml /app/ -COPY ./apps/run-service/package.json /app/apps/run-service/ -COPY ./packages/tsconfig /app/packages/tsconfig/ -COPY ./packages/utilities /app/packages/utilities/ -COPY ./packages/filemod /app/packages/filemod/ -RUN pnpm install +FROM base AS builder +WORKDIR /app -COPY ./apps/run-service/tsconfig.json /app/apps/run-service/ -COPY ./apps/run-service/esbuild.config.js /app/apps/run-service/ -COPY ./apps/run-service/src /app/apps/run-service/src/ +RUN apk update +RUN apk add --no-cache libc6-compat -RUN pnpm build --filter @codemod-com/utilities --filter @codemod-com/run-service +COPY package.json turbo.json pnpm-workspace.yaml pnpm-lock.yaml /app/ +COPY apps/run-service /app/apps/run-service/ -FROM node:20-alpine3.16 +COPY packages/tsconfig /app/packages/tsconfig/ +COPY packages/utilities /app/packages/utilities/ +COPY packages/filemod /app/packages/filemod/ +COPY packages/telemetry /app/packages/telemetry/ +COPY packages/database /app/packages/database/ +COPY packages/runner /app/packages/runner/ +COPY packages/printer /app/packages/printer/ +COPY packages/workflow /app/packages/workflow/ +COPY packages/api-types /app/packages/api-types/ +COPY packages/codemod-utils /app/packages/codemod-utils/ +COPY packages/auth /app/packages/auth/ -RUN npm install -g pnpm +RUN turbo prune @codemod-com/run-service --docker + + +FROM base AS installer WORKDIR /app -COPY --from=builder /app/apps/run-service/package.json /app -COPY --from=builder /app/apps/run-service/build /app/build -COPY --from=builder /app/packages /app/packages +RUN apk update +RUN apk add --no-cache libc6-compat + +# First install the dependencies (as they change less often) +COPY --from=builder /app/out/json/ . +RUN pnpm install + +# Build the project +COPY --from=builder /app/out/full/ . +RUN pnpm build --filter @codemod-com/run-service + -EXPOSE 8086 -CMD node build/index.js +FROM base AS runner +# Copy stuff for prisma to work +COPY --from=installer /app/node_modules/.pnpm/@prisma+client@5.15.1_prisma@5.15.1/node_modules/.prisma/ ./node_modules/.pnpm/@prisma+client@5.15.1_prisma@5.15.1/node_modules/.prisma/ + +WORKDIR /app +COPY --from=installer /app/apps/run-service ./apps/run-service +COPY --from=installer /app/packages/database ./packages/database + +CMD cd packages/database && pnpm dlx prisma@latest migrate deploy && cd - && node apps/run-service/build/index.js diff --git a/apps/run-service/package.json b/apps/run-service/package.json index fd4d2e58e..1a6ed6613 100644 --- a/apps/run-service/package.json +++ b/apps/run-service/package.json @@ -1,6 +1,6 @@ { "name": "@codemod-com/run-service", - "version": "0.0.3", + "version": "0.0.5", "scripts": { "build": "tsc && node esbuild.config.js", "start": "node build/index.js", @@ -9,6 +9,7 @@ "author": "Codemod inc.", "private": true, "devDependencies": { + "@codemod-com/api-types": "workspace:*", "@codemod-com/tsconfig": "workspace:*", "@types/node": "20.10.5", "@types/parse-github-url": "catalog:", @@ -21,6 +22,7 @@ }, "dependencies": { "@codemod-com/auth": "workspace:*", + "@codemod-com/database": "workspace:*", "@codemod-com/utilities": "workspace:*", "@fastify/busboy": "catalog:", "@fastify/cors": "catalog:", diff --git a/apps/run-service/src/schemata/env.ts b/apps/run-service/src/schemata/env.ts index 80d2da7f7..1fa007ae5 100644 --- a/apps/run-service/src/schemata/env.ts +++ b/apps/run-service/src/schemata/env.ts @@ -10,6 +10,8 @@ import { export const environmentSchema = object({ PORT: pipe(string(), transform(Number)), + NODE_ENV: string(), + DATABASE_URI: string(), REDIS_HOST: optional(string()), REDIS_PORT: optional(string()), TASK_MANAGER_QUEUE_NAME: optional(string()), diff --git a/apps/run-service/src/schemata/schema.ts b/apps/run-service/src/schemata/schema.ts index 12dcd6537..72648ab4e 100644 --- a/apps/run-service/src/schemata/schema.ts +++ b/apps/run-service/src/schemata/schema.ts @@ -1,31 +1,15 @@ import { codemodRunBodySchema, + codemodRunStatusSchema, validateCodemodStatusParamsSchema, } from "@codemod-com/utilities"; - import { parse } from "valibot"; -export const parseCodemodRunBody = (input: unknown) => { - const { codemodArguments, ...rest } = parse(codemodRunBodySchema, input); - let argumentsJson: - | { - [key: string]: - | string - | number - | boolean - | (string | number | boolean)[]; - } - | undefined; - try { - if (codemodArguments) { - argumentsJson = codemodArguments && (JSON.parse(codemodArguments) as any); - } - } catch (e) { - // - } - - return { ...rest, codemodArguments: argumentsJson }; -}; +export const parseCodemodRunBody = (input: unknown) => + parse(codemodRunBodySchema, input); export const parseCodemodStatusParams = (input: unknown) => parse(validateCodemodStatusParamsSchema, input); + +export const parseCodemodStatusData = (input: unknown) => + parse(codemodRunStatusSchema, input); diff --git a/apps/run-service/src/server.ts b/apps/run-service/src/server.ts index bce5ff8c0..b377475b5 100644 --- a/apps/run-service/src/server.ts +++ b/apps/run-service/src/server.ts @@ -3,13 +3,21 @@ import fastifyMultipart from "@fastify/multipart"; import fastifyRateLimit from "@fastify/rate-limit"; import Fastify, { type FastifyPluginCallback } from "fastify"; +import { type ApiResponse, UNAUTHORIZED } from "@codemod-com/api-types"; import { type UserDataPopulatedRequest, getAuthPlugin, } from "@codemod-com/auth"; - +import { prisma } from "@codemod-com/database"; +import type { + CodemodRunJobData, + CodemodRunResponse, + CodemodRunStatus, + CodemodRunStatusResponse, +} from "@codemod-com/utilities"; import { parseCodemodRunBody, + parseCodemodStatusData, parseCodemodStatusParams, } from "./schemata/schema.js"; import { queue, redis } from "./services/Redis.js"; @@ -127,7 +135,7 @@ const routes: FastifyPluginCallback = (instance, _opts, done) => { return { version: packageJson.default.version }; }); - instance.post( + instance.post<{ Reply: ApiResponse }>( "/codemodRun", { preHandler: [instance.authenticate, instance.getUserData] }, async (request: UserDataPopulatedRequest, reply) => { @@ -137,38 +145,59 @@ const routes: FastifyPluginCallback = (instance, _opts, done) => { const userId = request.user?.id; if (!userId) { - return reply.code(401).send(); - } - - const { - codemodSource, - codemodEngine, - repoUrl, - branch, - persistent, - codemodArguments, - } = parseCodemodRunBody(request.body); - - const job = await queue.add(TaskManagerJobs.CODEMOD_RUN, { - codemodSource, - codemodEngine, - userId, - repoUrl, - branch, - persistent, - codemodArguments, - }); - - if (!job.id) { - return reply.code(500).send(); + return reply + .code(401) + .send({ error: UNAUTHORIZED, errorText: "Unauthorized" }); } - reply.type("application/json").code(200); - return { success: true, codemodRunId: job.id }; + const { codemods, repoUrl, branch } = parseCodemodRunBody(request.body); + + const created: (CodemodRunResponse["data"][number] | null)[] = + await Promise.all( + codemods.map(async (codemod) => { + if (!queue) return null; + + const data = { + userId, + repoUrl, + branch, + ...codemod, + } satisfies CodemodRunJobData; + + const job = await queue.add(TaskManagerJobs.CODEMOD_RUN, data); + + if (!job.id) return null; + await prisma.codemodRun.create({ + data: { + jobId: job.id, + ownerId: userId, + data: { + status: "progress", + codemod, + id: job.id, + progress: { + processed: 0, + total: 0, + percentage: 0, + }, + }, + repoUrl, + branch, + }, + }); + + return { jobId: job.id, codemodName: codemod.name }; + }), + ); + + return reply + .type("application/json") + .code(200) + .send({ success: true, data: created.filter(Boolean) }); }, ); - instance.get( + instance.get<{ Reply: CodemodRunStatusResponse }>( "/codemodRun/status/:jobId", { preHandler: instance.authenticate }, async (request, reply) => { @@ -176,44 +205,36 @@ const routes: FastifyPluginCallback = (instance, _opts, done) => { throw new Error("Redis service is not running."); } - const { jobId } = parseCodemodStatusParams(request.params); - - const data = await redis.get(`job-${jobId}::status`); - reply.type("application/json").code(200); - return { - success: true, - result: data - ? (JSON.parse(data) as { - status: string; - message?: string; - link?: string; - progress?: { processed: number; total: number }; - }) - : null, - }; - }, - ); - - instance.get( - "/codemodRun/output/:jobId", - { preHandler: instance.authenticate }, - async (request, reply) => { - if (!redis) { - throw new Error("Redis service is not running."); - } - - const { jobId } = parseCodemodStatusParams(request.params); - - const job = await queue?.getJob(jobId); - - const data = job?.data.persistent - ? await redis.get(`job-${jobId}::output`) - : await redis.getdel(`job-${jobId}::output`); - reply.type("application/json").code(200); - return { - success: true, - output: data, - }; + const { ids } = parseCodemodStatusParams(request.params); + + const data: (CodemodRunStatus | null)[] = await Promise.all( + ids.map(async (id) => { + if (!redis) return null; + const redisData = await redis.get(`job-${id}::status`); + if (!redisData) return null; + + let parsed: CodemodRunStatus; + try { + parsed = parseCodemodStatusData(JSON.parse(redisData)); + } catch (err) { + return null; + } + + if (parsed.status === "done" || parsed.status === "error") { + await prisma.codemodRun.update({ + where: { jobId: id }, + data: { data: parsed }, + }); + } + + return parsed; + }), + ); + + return reply + .type("application/json") + .code(200) + .send({ success: true, data: data.filter(Boolean) }); }, ); diff --git a/apps/run-service/src/services/Redis.ts b/apps/run-service/src/services/Redis.ts index a3b648809..134dd26ff 100644 --- a/apps/run-service/src/services/Redis.ts +++ b/apps/run-service/src/services/Redis.ts @@ -1,9 +1,13 @@ +import type { CodemodRunJobData } from "@codemod-com/utilities"; import { Queue } from "bullmq"; import { Redis } from "ioredis"; - -import type { parseCodemodRunBody } from "../schemata/schema.js"; import { environment } from "../util.js"; +const TASK_MANAGER_QUEUE_NAME = + environment.NODE_ENV === "staging" + ? `${environment.TASK_MANAGER_QUEUE_NAME}_STAGING` + : environment.TASK_MANAGER_QUEUE_NAME; + export const redis = environment.REDIS_HOST ? new Redis({ host: String(environment.REDIS_HOST), @@ -12,12 +16,8 @@ export const redis = environment.REDIS_HOST }) : null; -type CodemodRunJob = ReturnType & { - userId: string; -}; - export const queue = redis - ? new Queue(environment.TASK_MANAGER_QUEUE_NAME ?? "", { + ? new Queue(TASK_MANAGER_QUEUE_NAME ?? "", { connection: redis, }) : null; diff --git a/apps/run-service/src/util.ts b/apps/run-service/src/util.ts index a20c865db..4ab658a84 100644 --- a/apps/run-service/src/util.ts +++ b/apps/run-service/src/util.ts @@ -1,10 +1,3 @@ import { parseEnvironment } from "./schemata/env.js"; -export const buildTimeoutPromise = (ms: number) => - new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, ms); - }); - export const environment = parseEnvironment(process.env); diff --git a/apps/shared/mocks.ts b/apps/shared/mocks.ts new file mode 100644 index 000000000..67558fa98 --- /dev/null +++ b/apps/shared/mocks.ts @@ -0,0 +1,7 @@ +import { mockedGhRunEndpoints } from "./mocks/gh-run"; +import { mockedWorkflowRunEndpoints } from "./mocks/workflow-runs"; + +export const mockedEndpoints = { + ...mockedGhRunEndpoints, + ...mockedWorkflowRunEndpoints, +}; diff --git a/packages/api-types/src/campaign.ts b/packages/api-types/src/campaign.ts new file mode 100644 index 000000000..a8c36a1da --- /dev/null +++ b/packages/api-types/src/campaign.ts @@ -0,0 +1,5 @@ +export type Campaign = { + title: string; + owner: string; + updatedAt: string; +}; diff --git a/packages/api-types/src/github.ts b/packages/api-types/src/github.ts index 9878db474..f7b3a1898 100644 --- a/packages/api-types/src/github.ts +++ b/packages/api-types/src/github.ts @@ -78,37 +78,3 @@ export type GithubContent = { url: string; download_url: string | null; }; - -export type Result = - | { - status: "progress" | "error"; - message: string; // internal events (crating folders, cloning repo, creating pull request etc..) | error messages - } - | { - status: "executing codemod"; - progress: { processed: number; total: number }; - } - | { - status: "done"; - link: string; // PR Link - }; - -export type GetExecutionStatusResponse = Readonly<{ - result: Result | null; - success: boolean; -}>; - -export type GetExecutionStatusRequest = Readonly<{ - token?: string | null; - executionId?: string | null; -}>; - -export type CodemodRunStatus = { codemodRunId: string; success: boolean }; - -export type CodemodRunRequest = { - codemodEngine: "jscodeshift" | "ts-morph"; - repoUrl: string; - codemodSource: string; - codemodName: string; - branch: string; -}; diff --git a/packages/api-types/src/index.ts b/packages/api-types/src/index.ts index dc9c72495..8b3f4b04d 100644 --- a/packages/api-types/src/index.ts +++ b/packages/api-types/src/index.ts @@ -1,4 +1,5 @@ +export * from "./campaign.js"; +export * from "./clerk.js"; export * from "./errors.js"; -export * from "./responses.js"; export * from "./github.js"; -export * from "./clerk.js"; +export * from "./responses.js"; diff --git a/packages/database/prisma/migrations/20240829154420_insights/migration.sql b/packages/database/prisma/migrations/20240829154420_insights/migration.sql new file mode 100644 index 000000000..9a47edd63 --- /dev/null +++ b/packages/database/prisma/migrations/20240829154420_insights/migration.sql @@ -0,0 +1,47 @@ +-- CreateTable +CREATE TABLE "CodemodRun" ( + "jobId" VARCHAR(255) NOT NULL, + "data" JSONB NOT NULL, + "ownerId" VARCHAR(255) NOT NULL, + "repoUrl" TEXT, + "branch" TEXT, + "insightId" INTEGER, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CodemodRun_pkey" PRIMARY KEY ("jobId") +); + +-- CreateTable +CREATE TABLE "Insight" ( + "id" SERIAL NOT NULL, + "name" VARCHAR(255), + "description" TEXT, + "ownerId" VARCHAR(255) NOT NULL, + "repoUrls" TEXT[], + "tags" TEXT[], + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Insight_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Widget" ( + "id" SERIAL NOT NULL, + "kind" TEXT NOT NULL, + "title" TEXT NOT NULL, + "data" JSONB NOT NULL, + "template" BOOLEAN NOT NULL DEFAULT false, + "insightId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Widget_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "CodemodRun" ADD CONSTRAINT "CodemodRun_insightId_fkey" FOREIGN KEY ("insightId") REFERENCES "Insight"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Widget" ADD CONSTRAINT "Widget_insightId_fkey" FOREIGN KEY ("insightId") REFERENCES "Insight"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index c41f22ae5..f97b314e3 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -91,3 +91,50 @@ model CodeDiff { createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt } + +model CodemodRun { + jobId String @id @db.VarChar(255) + /// [RunStatus] + data Json + ownerId String @db.VarChar(255) + + repoUrl String? + branch String? + + insight Insight? @relation(fields: [insightId], references: [id]) + insightId Int? + + updatedAt DateTime @default(now()) @updatedAt + createdAt DateTime @default(now()) +} + +model Insight { + id Int @id @default(autoincrement()) + name String? @db.VarChar(255) + description String? @db.Text + ownerId String @db.VarChar(255) + repoUrls String[] + tags String[] + + codemodRuns CodemodRun[] + widgets Widget[] + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt +} + +model Widget { + id Int @id @default(autoincrement()) + // chart | table | primitive + kind String + // Frontend input + title String + /// [WidgetData] + data Json + template Boolean @default(false) + + insight Insight @relation(fields: [insightId], references: [id]) + insightId Int + + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt +} diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index d187c79a8..3972480db 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -1,4 +1,8 @@ -import type { CodemodConfig } from "@codemod-com/utilities"; +import type { + CodemodConfig, + CodemodRunStatus, + WidgetData as WidgetDataInput, +} from "@codemod-com/utilities"; import { PrismaClient } from "@prisma/client"; declare global { @@ -6,6 +10,8 @@ declare global { namespace PrismaJson { type Argument = CodemodConfig["arguments"]; type Applicability = CodemodConfig["applicability"]; + type RunStatus = CodemodRunStatus; + type WidgetData = WidgetDataInput; } } diff --git a/packages/database/src/seed.ts b/packages/database/src/seed.ts index 0decc8644..c48560c33 100644 --- a/packages/database/src/seed.ts +++ b/packages/database/src/seed.ts @@ -187,11 +187,562 @@ async function seedDatabaseWithCodemods(): Promise { ); } +async function seedDatabaseWithInsightsAndWidgets(): Promise { + await Promise.all( + Array.from({ length: getRandomNumber(4, 7) }, async () => { + const ownerId = "1"; + const framework = getRandomFramework(); + const repoUrls = [getRandomUrl(), getRandomUrl(), getRandomUrl()]; + + await prisma.insight.create({ + data: { + name: `${framework} Migration`, + description: `Migrate to ${framework} 18.3.1`, + ownerId, + repoUrls, + tags: ["Migration", "Cleanup"], + codemodRuns: { + createMany: { + data: repoUrls.flatMap((repoUrl, i) => [ + { + repoUrl, + ownerId, + branch: "main", + data: { + status: "done", + codemod: { + engine: "workflow", + name: "drift_analyzer", + args: { repos: repoUrl }, + }, + id: faker.string.uuid(), + data: JSON.stringify([ + { + name: "@codemod-com/studio", + drift: 1.689288623311909, + timestamp: "2024-02-23T12:59:21.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/studio", + drift: 1.7248814144027598, + timestamp: "2024-03-07T16:00:18.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/studio", + drift: 1.7467846704586678, + timestamp: "2024-03-15T18:53:00.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3915207019993566, + timestamp: "2024-03-15T18:53:00.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/studio", + drift: 1.7796395545425299, + timestamp: "2024-03-27T12:34:24.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3915207019993566, + timestamp: "2024-03-27T12:34:24.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/studio", + drift: 1.8152323456333805, + timestamp: "2024-04-09T13:54:03.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3915207019993566, + timestamp: "2024-04-09T13:54:03.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/studio", + drift: 1.8426114157032656, + timestamp: "2024-04-18T16:18:22.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3915207019993566, + timestamp: "2024-04-18T16:18:22.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.8617767647521852, + timestamp: "2024-04-26T13:54:15.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.6379323326283223, + timestamp: "2024-04-26T13:54:15.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.8918937418290587, + timestamp: "2024-05-07T14:21:21.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.8268479161105293, + timestamp: "2024-05-07T14:21:21.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3942586090063451, + timestamp: "2024-05-15T11:07:05.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.9137969978849667, + timestamp: "2024-05-15T11:07:05.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.9302244399268977, + timestamp: "2024-05-21T10:31:00.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3942586090063451, + timestamp: "2024-05-21T10:31:00.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.941176067954852, + timestamp: "2024-05-27T17:44:54.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3942586090063451, + timestamp: "2024-05-27T17:44:54.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.957603509996783, + timestamp: "2024-05-31T14:37:38.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3942586090063451, + timestamp: "2024-05-31T14:37:38.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 1.990458394080645, + timestamp: "2024-06-11T16:11:14.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.3942586090063451, + timestamp: "2024-06-11T16:11:14.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.012361650136553, + timestamp: "2024-06-21T07:56:38.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.49829907527190836, + timestamp: "2024-06-21T07:56:38.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.034264906192461, + timestamp: "2024-06-28T14:22:22.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.5366297733697475, + timestamp: "2024-06-28T14:22:22.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.0725956042903, + timestamp: "2024-07-12T12:24:06.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.539367680376736, + timestamp: "2024-07-12T12:24:06.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.1081883953811507, + timestamp: "2024-07-24T19:02:49.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.539367680376736, + timestamp: "2024-07-24T19:02:49.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.14651909347899, + timestamp: "2024-08-08T15:54:34.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.539367680376736, + timestamp: "2024-08-08T15:54:34.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/frontend", + drift: 2.20127723361876, + timestamp: "2024-08-27T15:47:25.000Z", + label: "real_drift", + }, + { + name: "@codemod-com/backend", + drift: 0.539367680376736, + timestamp: "2024-08-27T15:47:25.000Z", + label: "real_drift", + }, + ]), + progress: { total: 100, processed: 50, percentage: 50 }, + }, + jobId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + }, + { + repoUrl, + ownerId, + branch: "main", + data: { + status: "done", + codemod: { + engine: "workflow", + name: "drift_analyzer_pkg", + args: { repos: repoUrl }, + }, + data: JSON.stringify([ + { + repoName: "https://github.com/stackbit/stackbit-app", + behind: "1 minor", + drift: 0.3942586090063451, + packageName: "ansi-to-html", + packageJsonName: "stackbit-app", + specifier: "ansi-to-html@^0.6.14", + declared: "^0.6.14", + installed: "0.6.15", + latest: "0.7.2", + }, + { + repoName: "https://github.com/stackbit/stackbit-app", + behind: "1 major", + drift: 1.8699904857731506, + packageName: "remark-gfm", + packageJsonName: "stackbit-app", + specifier: "remark-gfm@3.0.1", + declared: "3.0.1", + installed: "3.0.1", + latest: "4.0.0", + }, + { + repoName: "https://github.com/stackbit/stackbit-app", + behind: "1 major", + drift: 0.3395004688665749, + packageName: "remark-parse", + packageJsonName: "stackbit-app", + specifier: "remark-parse@10.0.2", + declared: "10.0.2", + installed: "10.0.2", + latest: "11.0.0", + }, + { + repoName: "https://github.com/stackbit/stackbit-app", + behind: "1 major", + drift: 0.005475814013977016, + packageName: "@types/history", + packageJsonName: "stackbit-app", + specifier: "@types/history@4.7.11", + declared: "4.7.11", + installed: "4.7.11", + latest: "5.0.0", + }, + ]), + id: faker.string.uuid(), + progress: { total: 100, processed: 100, percentage: 100 }, + }, + jobId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + }, + { + repoUrl, + ownerId, + branch: "main", + data: { + status: "done", + codemod: { + engine: "workflow", + name: "prs_tracker", + args: { + repos: repoUrl, + packageNames: "@codemod-com/codemod", + }, + }, + data: JSON.stringify([ + { + timestamp: faker.date.anytime(), + task: faker.lorem.words(3), + status: "In Review", + pr: { + url: faker.internet.url(), + name: faker.lorem.words(3), + }, + reviewer: { + img: faker.image.avatar(), + name: faker.person.fullName(), + }, + filesChanged: 3, + timeSaved: "1h 45m", + }, + { + timestamp: faker.date.anytime(), + task: faker.lorem.words(3), + status: "Merged", + pr: { + url: faker.internet.url(), + name: faker.lorem.words(3), + }, + reviewer: { + img: faker.image.avatar(), + name: faker.person.fullName(), + }, + filesChanged: 55, + timeSaved: "13h 10m", + }, + ]), + id: faker.string.uuid(), + progress: { total: 100, processed: 100, percentage: 100 }, + }, + jobId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + }, + { + repoUrl, + ownerId, + branch: "main", + data: { + status: "done", + codemod: { + engine: "workflow", + name: "timesave_analyzer", + args: { repos: repoUrl }, + }, + id: faker.string.uuid(), + data: JSON.stringify([ + { + timestamp: faker.date.anytime(), + savedTimeFormatted: "1h 45m", + }, + { + timestamp: faker.date.anytime(), + savedTimeFormatted: "1h 30m", + }, + ]), + progress: { total: 100, processed: 50, percentage: 50 }, + }, + jobId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + }, + { + repoUrl, + ownerId, + branch: "main", + data: { + status: "done", + codemod: { + engine: "workflow", + name: "chart_analyzer", + args: { repos: repoUrl }, + }, + id: faker.string.uuid(), + data: JSON.stringify([ + { + timestamp: faker.date.anytime(), + use: 0, + useContext: 47, + }, + { + timestamp: faker.date.anytime(), + use: 11, + useContext: 42, + }, + { + timestamp: faker.date.anytime(), + use: 17, + useContext: 29, + }, + { + timestamp: faker.date.anytime(), + use: 21, + useContext: 23, + }, + { + timestamp: faker.date.anytime(), + use: 25, + useContext: 20, + }, + { + timestamp: faker.date.anytime(), + use: 33, + useContext: 11, + }, + ]), + progress: { total: 100, processed: 50, percentage: 50 }, + }, + jobId: faker.string.uuid(), + createdAt: new Date(), + updatedAt: new Date(), + }, + ]), + }, + }, + widgets: { + createMany: { + data: [ + { + kind: "primitive", + data: { + text: "Hello world, I am from timesave_analyzer. `timesave_analyzer.savedTimeFormatted`", + }, + title: "Time saved with Codemod", + }, + { + kind: "chart", + data: { + y: [ + { + title: "use (React 19)", + value: "`chart_analyzer.use`", + color: "#F59E0B", + }, + { + title: "useContext (React 18)", + value: "`chart_analyzer.useContext`", + color: "#FCD34D", + }, + ], + x: "`chart_analyzer.timestamp`", + }, + title: "My chart", + }, + { + kind: "table", + title: "Drift (libyears)", + data: [ + { + title: "Name", + value: "`drift_analyzer_pkg.packageName`", + }, + { + title: "Current version", + value: "`drift_analyzer_pkg.installed`", + }, + { + title: "Stable version", + value: "`drift_analyzer_pkg.latest`", + }, + { title: "Behind", value: "`drift_analyzer_pkg.behind`" }, + { title: "Drift", value: "`drift_analyzer_pkg.drift`" }, + ], + }, + { + kind: "table", + title: "PRs tracker", + data: [ + { + title: "Task", + value: "`prs_tracker.task`", + }, + { + title: "PR", + value: "`prs_tracker.pr.url`", + }, + { + title: "PR status", + description: "Status of the PR", + value: "`prs_tracker.status`", + }, + { + title: "Reviewer", + value: "`prs_tracker.reviewer.name`", + }, + { + title: "Files changed", + value: "`prs_tracker.filesChanged`", + }, + { + title: "Time saved", + description: "Estimated time saving from this PR", + value: "`prs_tracker.timeSaved`", + }, + ], + }, + { + kind: "primitive", + data: { + heading: "`timesave_analyzer.savedTimeFormatted`", + description: + "`timesave_analyzer.lastMonthPercentage` better than last month", + }, + title: "Time saved with Codemod", + }, + ], + }, + }, + }, + }); + }), + ); +} + async function main() { try { await seedDatabaseWithFrameworks(); await seedDatabaseWithCategories(); await seedDatabaseWithCodemods(); + await seedDatabaseWithInsightsAndWidgets(); console.log("Database seeded successfully!"); } catch (error) { console.error(error); diff --git a/packages/runner/src/source-code.ts b/packages/runner/src/source-code.ts index 2f9d2392c..9acbac1e8 100644 --- a/packages/runner/src/source-code.ts +++ b/packages/runner/src/source-code.ts @@ -69,15 +69,13 @@ export const getTransformer = async (source: string, name?: string) => { const transformer = typeof module.default === "function" ? module.default - : module.__esModule - ? module.default ?? - module.transform ?? - module.handleSourceFile ?? - module.repomod ?? - module.filemod ?? - module.workflow ?? - null - : null; + : module.default ?? + module.transform ?? + module.handleSourceFile ?? + module.repomod ?? + module.filemod ?? + module.workflow ?? + null; temporaryLoadedModules.set(hashDigest, transformer); return transformer; diff --git a/packages/utilities/src/functions/codemods.ts b/packages/utilities/src/functions/codemods.ts index 9c2ce1acb..0eddf539c 100644 --- a/packages/utilities/src/functions/codemods.ts +++ b/packages/utilities/src/functions/codemods.ts @@ -90,7 +90,7 @@ export async function getEntryPath(options: { globPattern = "**/rule.yaml"; break; default: - globPattern = "src/index.{ts,js}"; + globPattern = "src/index.{ts,js,mts,mjs}"; break; } diff --git a/packages/utilities/src/index.ts b/packages/utilities/src/index.ts index b53465b00..c5a0fdbb9 100644 --- a/packages/utilities/src/index.ts +++ b/packages/utilities/src/index.ts @@ -17,4 +17,5 @@ export * from "./schemata/codemod-config.js"; export * from "./schemata/codemod.js"; export * from "./schemata/engine-options.js"; export * from "./schemata/file-commands.js"; -export * from "./schemata/github-run.js"; +export * from "./schemata/codemod-run.js"; +export * from "./schemata/insights.js"; diff --git a/packages/utilities/src/schemata/codemod-run.ts b/packages/utilities/src/schemata/codemod-run.ts new file mode 100644 index 000000000..b17f8150b --- /dev/null +++ b/packages/utilities/src/schemata/codemod-run.ts @@ -0,0 +1,72 @@ +import * as v from "valibot"; + +export const codemodObjectSchema = v.object({ + engine: v.union([ + v.literal("jscodeshift"), + v.literal("ts-morph"), + v.literal("workflow"), + ]), + name: v.string(), + source: v.optional(v.string()), + args: v.optional(v.record(v.string(), v.any())), +}); + +export const codemodRunBodySchema = v.object({ + codemods: v.array(codemodObjectSchema), + repoUrl: v.optional(v.string()), + branch: v.optional(v.string()), +}); + +export type CodemodRunBody = v.InferInput; + +export const validateCodemodStatusParamsSchema = v.object({ + ids: v.array(v.string()), +}); + +export type ExecutionProgress = v.InferOutput; +const executionProgressSchema = v.object({ + processed: v.number(), + total: v.number(), + percentage: v.number(), +}); + +export type CodemodRunStatusInput = v.InferInput; +export type CodemodRunStatus = v.InferOutput; +export const codemodRunStatusSchema = v.object({ + // job id + id: v.string(), + // progress from CLI + progress: executionProgressSchema, + codemod: codemodObjectSchema, + status: v.union([ + v.literal("progress"), + v.literal("error"), + v.literal("done"), + ]), + data: v.optional(v.string()), +}); + +export type CodemodRunJobData = { + userId: string; + engine: "jscodeshift" | "ts-morph" | "workflow"; + repoUrl?: string; + branch?: string; + createPullRequest?: boolean; + name: string; + source?: string; +}; + +export type CodemodRunRequestPayload = Omit; + +// export type GetExecutionStatusRequest = Readonly<{ +// token?: string | null; +// executionId?: string | null; +// }>; +export type CodemodRunResponse = { + success: true; + data: { jobId: string; codemodName: string }[]; +}; +export type CodemodRunStatusResponse = { + success: true; + data: CodemodRunStatus[]; +}; diff --git a/packages/utilities/src/schemata/github-run.ts b/packages/utilities/src/schemata/github-run.ts deleted file mode 100644 index 93f2c2eb0..000000000 --- a/packages/utilities/src/schemata/github-run.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as v from "valibot"; - -export const codemodRunBodySchema = v.object({ - codemodSource: v.string(), - codemodEngine: v.union([ - v.literal("jscodeshift"), - v.literal("ts-morph"), - v.literal("workflow"), - ]), - codemodArguments: v.optional(v.string()), - repoUrl: v.optional(v.string()), - branch: v.optional(v.string()), - persistent: v.optional(v.boolean()), -}); - -export type CodemodRunResponse = { success: boolean; codemodRunId: string }; - -export const validateCodemodStatusParamsSchema = v.object({ - jobId: v.string(), -}); diff --git a/packages/utilities/src/schemata/insights.ts b/packages/utilities/src/schemata/insights.ts new file mode 100644 index 000000000..512285040 --- /dev/null +++ b/packages/utilities/src/schemata/insights.ts @@ -0,0 +1,93 @@ +import * as v from "valibot"; + +export type PrimitiveWidgetData = v.InferInput< + typeof primitiveWidgetDataSchema +>; +export const primitiveWidgetDataSchema = v.object({ + heading: v.optional(v.string()), + text: v.optional(v.string()), + description: v.optional(v.string()), +}); + +export type TableWidgetData = v.InferInput; +export const tableWidgetDataSchema = v.array( + v.object({ + title: v.string(), + value: v.string(), + description: v.optional(v.string()), + icon: v.optional(v.string()), + color: v.optional(v.string()), + }), +); + +export type ChartWidgetData = v.InferInput; +export const chartWidgetDataSchema = v.object({ + y: v.array( + v.object({ + title: v.string(), + value: v.string(), + color: v.optional(v.string()), + }), + ), + x: v.string(), +}); + +export type WidgetData = v.InferInput; +export const widgetDataSchema = v.union([ + primitiveWidgetDataSchema, + tableWidgetDataSchema, + chartWidgetDataSchema, +]); + +export const putWidgetBodySchema = v.union([ + v.object({ + title: v.string(), + insightId: v.number(), + data: primitiveWidgetDataSchema, + kind: v.literal("primitive"), + }), + v.object({ + title: v.string(), + insightId: v.number(), + data: tableWidgetDataSchema, + kind: v.literal("table"), + }), + v.object({ + title: v.string(), + insightId: v.number(), + data: chartWidgetDataSchema, + kind: v.literal("chart"), + }), + v.object({ + id: v.number(), + title: v.optional(v.string()), + data: v.optional(widgetDataSchema), + kind: v.optional( + v.union([v.literal("primitive"), v.literal("table"), v.literal("chart")]), + ), + }), +]); + +export type PutWidgetBody = v.InferInput; +export const parsePutWidgetBody = (input: unknown) => + v.parse(putWidgetBodySchema, input); + +export const postInsightBodySchema = v.object({ + repoUrls: v.array(v.string()), +}); + +export type PostInsightBody = v.InferInput; +export const parsePostInsightBody = (input: unknown) => + v.parse(postInsightBodySchema, input); + +export const putInsightBodySchema = v.object({ + id: v.optional(v.number()), + name: v.optional(v.string()), + description: v.optional(v.string()), + repoUrls: v.array(v.string()), + tags: v.array(v.string()), +}); + +export type PutInsightBody = v.InferInput; +export const parsePutInsightBody = (input: unknown) => + v.parse(putInsightBodySchema, input); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 734689b55..edeb76453 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -535,8 +535,8 @@ catalogs: specifier: ^3.0.0 version: 3.1.0 lucide-react: - specifier: ^0.265.0 - version: 0.265.0 + specifier: ^0.435.0 + version: 0.435.0 magic-string: specifier: ^0.30.10 version: 0.30.10 @@ -1038,7 +1038,7 @@ importers: version: 5.4.1 langchain: specifier: 'catalog:' - version: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) + version: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) openai: specifier: 'catalog:' version: 4.23.0 @@ -1299,6 +1299,9 @@ importers: '@codemod-com/api-types': specifier: workspace:* version: link:../../packages/api-types + '@codemod-com/database': + specifier: workspace:* + version: link:../../packages/database '@codemod-com/utilities': specifier: workspace:* version: link:../../packages/utilities @@ -1362,6 +1365,9 @@ importers: '@radix-ui/react-tooltip': specifier: 'catalog:' version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/themes': + specifier: ^3.1.1 + version: 3.1.3(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@sanity/client': specifier: ^6.21.0 version: 6.21.0(debug@3.2.7) @@ -1389,9 +1395,9 @@ importers: '@t3-oss/env-nextjs': specifier: 'catalog:' version: 0.7.3(typescript@5.5.3)(zod@3.22.3) - '@tailwindcss/typography': - specifier: 'catalog:' - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.3))) + '@tanstack/react-table': + specifier: ^8.20.1 + version: 8.20.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tinloof/sanity-studio': specifier: 'catalog:' version: 1.3.1(@sanity/client@6.21.0)(@sanity/mutator@3.47.1)(@types/node@20.14.8)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react-fast-compare@3.2.2)(react-is@18.3.1)(react@18.2.0)(rxjs@7.8.1)(sanity@3.47.1(@types/node@20.14.8)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(styled-components@6.1.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(terser@5.31.1))(styled-components@6.1.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(terser@5.31.1) @@ -1478,7 +1484,7 @@ importers: version: 2.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) lucide-react: specifier: 'catalog:' - version: 0.265.0(react@18.2.0) + version: 0.435.0(react@18.2.0) match-sorter: specifier: 'catalog:' version: 6.3.4 @@ -1527,6 +1533,9 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-grid-layout: + specifier: 1.3.4 + version: 1.3.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-hook-form: specifier: 'catalog:' version: 7.52.0(react@18.2.0) @@ -1536,6 +1545,9 @@ importers: react-markdown: specifier: 'catalog:' version: 8.0.7(@types/react@18.2.55)(react@18.2.0) + react-query: + specifier: ^3.39.3 + version: 3.39.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-resizable-panels: specifier: ^2.0.15 version: 2.0.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -1563,6 +1575,9 @@ importers: recast: specifier: 'catalog:' version: 0.22.0 + recharts: + specifier: ^2.12.7 + version: 2.12.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) rehype-sanitize: specifier: 'catalog:' version: 6.0.0 @@ -1642,6 +1657,9 @@ importers: '@babel/core': specifier: ^7.24.7 version: 7.24.7 + '@tailwindcss/typography': + specifier: 'catalog:' + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.3))) '@testing-library/dom': specifier: 'catalog:' version: 9.3.4 @@ -1678,6 +1696,9 @@ importers: '@types/react-dom': specifier: 'catalog:' version: 18.3.0 + '@types/react-grid-layout': + specifier: ^1.3.5 + version: 1.3.5 '@types/react-syntax-highlighter': specifier: 'catalog:' version: 15.5.13 @@ -1811,6 +1832,9 @@ importers: '@codemod-com/auth': specifier: workspace:* version: link:../../packages/auth + '@codemod-com/database': + specifier: workspace:* + version: link:../../packages/database '@codemod-com/utilities': specifier: workspace:* version: link:../../packages/utilities @@ -1848,6 +1872,9 @@ importers: specifier: 'catalog:' version: 0.34.0 devDependencies: + '@codemod-com/api-types': + specifier: workspace:* + version: link:../../packages/api-types '@codemod-com/tsconfig': specifier: workspace:* version: link:../../packages/tsconfig @@ -6262,8 +6289,6 @@ importers: specifier: ^4.11.0 version: 4.15.7 - packages/database/generated/client: {} - packages/deprecated: {} packages/filemod: @@ -10090,15 +10115,34 @@ packages: '@prisma/get-platform@5.15.1': resolution: {integrity: sha512-oFccp7bYys+ZYkmtYzjR+0cRrGKvSuF+h5QhSkyEsYQ9kzJzQRvuWt2SiHRPt8xOQ4MTmujM+bP5uOexnnAHdQ==} + '@radix-ui/colors@3.0.0': + resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} + '@radix-ui/number@1.0.1': resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} '@radix-ui/primitive@1.1.0': resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + '@radix-ui/react-accessible-icon@1.1.0': + resolution: {integrity: sha512-i9Zg4NOSXlfUva0agzI2DjWrvFJm9uO4L6CMW7nmMa5CIOOX/Yin894W7WwjodFQWPwe5kmAJ4JF33R8slKI2g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-alert-dialog@1.1.1': resolution: {integrity: sha512-wmCoJwj7byuVuiLKqDLlX7ClSUU0vd9sdCeM+2Ls+uf13+cpSJoMgwysHq1SGVVkJj5Xn0XWi1NoRCdkMpr6Mw==} peerDependencies: @@ -10138,6 +10182,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-aspect-ratio@1.1.0': + resolution: {integrity: sha512-dP87DM/Y7jFlPgUZTlhx6FF5CEzOiaxp2rBCKlaXlpH5Ip/9Fg5zZ9lDOQ5o/MOfUlf36eak14zoWYpgcgGoOg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-avatar@1.1.0': resolution: {integrity: sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==} peerDependencies: @@ -10151,6 +10208,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-checkbox@1.1.0': + resolution: {integrity: sha512-3+kSzVfMONtP3B6CvaOrXLVTyGYws7tGmG5kOY0AfyH9sexkLytIwciNwjZhY0RoGOEbxI7bMS21XYB8H5itWQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.0.3': resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -10195,6 +10265,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.1': + resolution: {integrity: sha512-wvMKKIeb3eOrkJ96s722vcidZ+2ZNfcYZWBPRHIB1VWrF+fiF851Io6LX0kmK5wTDQFKdulCCKJk2c3SBaQHvA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-context@1.0.1': resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: @@ -10327,6 +10410,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-form@0.1.0': + resolution: {integrity: sha512-1/oVYPDjbFILOLIarcGcMKo+y6SbTVT/iUKVEw59CF4offwZgBgC3ZOeSBewjqU0vdA6FWTPWTN63obj55S/tQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-hover-card@1.1.1': + resolution: {integrity: sha512-IwzAOP97hQpDADYVKrEEHUH/b2LA+9MgB0LgdmnbFO2u/3M5hmEofjjr2M6CyzUblaAqJdFm6B7oFtU72DPXrA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-id@1.0.1': resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: @@ -10371,6 +10480,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-navigation-menu@1.2.0': + resolution: {integrity: sha512-OQ8tcwAOR0DhPlSY3e4VMXeHiol7la4PPdJWhhwJiJA+NLX0SaCaonOkRnI3gCDHoZ7Fo7bb/G6q25fRM2Y+3Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.1': + resolution: {integrity: sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.1.2': resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: @@ -10475,6 +10610,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-radio-group@1.2.0': + resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.1.0': resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} peerDependencies: @@ -10488,6 +10636,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-scroll-area@1.1.0': + resolution: {integrity: sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-select@1.2.2': resolution: {integrity: sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==} peerDependencies: @@ -10501,6 +10662,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-select@2.1.1': + resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-separator@1.1.0': resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==} peerDependencies: @@ -10514,6 +10688,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.2.0': + resolution: {integrity: sha512-dAHCDA4/ySXROEPaRtaMV5WHL8+JB/DbtyTbJjYkY0RXmKMO2Ln8DFZhywG5/mVQ4WqHDBc8smc14yPXPqZHYA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -10571,6 +10758,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-toggle-group@1.1.0': + resolution: {integrity: sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.0': + resolution: {integrity: sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.1.1': resolution: {integrity: sha512-LLE8nzNE4MzPMw3O2zlVlkLFid3y9hMUs7uCbSHyKSo+tCN4yMCf+ZCCcfrYgsOC0TiHBPQ1mtpJ2liY3ZT3SQ==} peerDependencies: @@ -10742,6 +10955,19 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@radix-ui/themes@3.1.3': + resolution: {integrity: sha512-GJt4r7Vh0w4yiGuqOKLvrZXLEAxUWfEUUtdj17rCfi+P/zpKUc7TsXro8GftA2Y1tm78Cx9x1HKt23dHc/WqIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: 16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@react-dnd/asap@4.0.1': resolution: {integrity: sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==} @@ -11599,8 +11825,8 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@tanstack/react-table@8.17.3': - resolution: {integrity: sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==} + '@tanstack/react-table@8.20.1': + resolution: {integrity: sha512-PJK+07qbengObe5l7c8vCdtefXm8cyR4i078acWrHbdm8JKw1ES7YpmOtVt9ALUVEEFAHscdVpGRhRgikgFMbQ==} engines: {node: '>=12'} peerDependencies: react: '>=16.8' @@ -11617,8 +11843,8 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@tanstack/table-core@8.17.3': - resolution: {integrity: sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==} + '@tanstack/table-core@8.20.1': + resolution: {integrity: sha512-5Ly5TIRHnWH7vSDell9B/OVyV380qqIJVg7H7R7jU4fPEmOD4smqAX7VRflpYI09srWR8aj5OLD2Ccs1pI5mTg==} engines: {node: '>=12'} '@tanstack/virtual-core@3.0.0-beta.54': @@ -11889,6 +12115,33 @@ packages: '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.0': + resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==} + + '@types/d3-scale@4.0.8': + resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} + + '@types/d3-shape@3.1.6': + resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} + + '@types/d3-time@3.0.3': + resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -12060,6 +12313,9 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-grid-layout@1.3.5': + resolution: {integrity: sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==} + '@types/react-is@18.3.0': resolution: {integrity: sha512-KZJpHUkAdzyKj/kUHJDc6N7KyidftICufJfOFpiG6haL/BDQNQt5i4n1XDUL/nDZAtGLHDSWRYpLzKTAKSvX6w==} @@ -12958,6 +13214,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + broadcast-channel@3.7.0: + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} + browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} @@ -13186,6 +13445,9 @@ packages: class-variance-authority@0.7.0: resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + classnames@2.3.2: + resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -13669,6 +13931,50 @@ packages: resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} engines: {node: '>= 10'} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + d@1.0.2: resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} engines: {node: '>=0.12'} @@ -13749,6 +14055,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -13866,6 +14175,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -14464,6 +14776,10 @@ packages: fast-equals@4.0.3: resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} + fast-equals@5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} + fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} @@ -15288,6 +15604,10 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + interpret@3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} @@ -15712,6 +16032,9 @@ packages: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + js-tiktoken@1.0.12: resolution: {integrity: sha512-L7wURW1fH9Qaext0VzaUDpFGVQgjkdE3Dgsy9/+yXyGEpBKnylTd0mU0bfbNkKDlXRb6TEsZkwuflu1B8uQbJQ==} @@ -16321,10 +16644,10 @@ packages: lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} - lucide-react@0.265.0: - resolution: {integrity: sha512-znyvziBEUQ7CKR31GiU4viomQbJrpDLG5ac+FajwiZIavC3YbPFLkzQx3dCXT4JWJx/pB34EwmtiZ0ElGZX0PA==} + lucide-react@0.435.0: + resolution: {integrity: sha512-we5GKfzjMDw9m9SsyZJvWim9qaT+Ya5kaRS+OGFqgLqXUrPM1h+7CiMw5pKdEIoaBqfXz2pyv9TASAdpIAJs0Q==} peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc luxon@3.4.4: resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} @@ -16709,6 +17032,9 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + microseconds@0.2.0: + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -16935,6 +17261,9 @@ packages: resolution: {integrity: sha512-zoTNyBafxG0+F5PP3T3j1PKMr7gedriSdYRhLFLRFCz0OnQfQ6BkVk9peXVF30hz633Bw0Zh5McleOrXPjWYCQ==} engines: {node: '>=18'} + nano-time@1.0.0: + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} + nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -17252,6 +17581,9 @@ packages: obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + oblivious-set@1.0.0: + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + observable-callback@1.0.3: resolution: {integrity: sha512-VlS275UyPnwdMtzxDgr/lCiOUyq9uXNll3vdwzDcJ6PB/LuO7gLmxAQopcCA3JoFwwujBwyA7/tP5TXZwWSXew==} engines: {node: '>=16'} @@ -18211,6 +18543,12 @@ packages: peerDependencies: react: ^18.2.0 + react-draggable@4.4.6: + resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + react-dropzone@11.7.1: resolution: {integrity: sha512-zxCMwhfPy1olUEbw3FLNPLhAm/HnaYH5aELIEglRbqabizKAdHs0h+WuyOpmA+v1JXn0++fpQDdNfUagWt5hJQ==} engines: {node: '>= 10.13'} @@ -18235,6 +18573,12 @@ packages: '@types/react': optional: true + react-grid-layout@1.3.4: + resolution: {integrity: sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + react-hook-form@7.52.0: resolution: {integrity: sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==} engines: {node: '>=12.22.0'} @@ -18282,6 +18626,18 @@ packages: '@types/react': '>=16' react: '>=16' + react-query@3.39.3: + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-redux@7.2.9: resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} peerDependencies: @@ -18324,6 +18680,16 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.4: + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-remove-scroll-bar@2.3.6: resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} engines: {node: '>=10'} @@ -18366,6 +18732,11 @@ packages: react: ^16.14.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 + react-resizable@3.0.5: + resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} + peerDependencies: + react: '>= 16.3' + react-rx@2.1.3: resolution: {integrity: sha512-4dppkgEFAldr75IUUz14WyxuI2cJhpXYrrIM+4gvG6slKzaMUCmcgiiykx9Hst0UmtwNt247nRoOFDmN0Q7GJw==} peerDependencies: @@ -18384,6 +18755,12 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-smooth@4.0.1: + resolution: {integrity: sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-style-proptype@3.2.2: resolution: {integrity: sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==} @@ -18558,6 +18935,16 @@ packages: resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==} engines: {node: '>= 4'} + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.12.7: + resolution: {integrity: sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + rechoir@0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} @@ -20170,6 +20557,9 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unload@2.2.0: + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} + unzipper@0.11.6: resolution: {integrity: sha512-anERl79akvqLbAxfjIFe4hK0wsi0fH4uGLwNEl4QEnG+KKs3QQeApYgOS/f6vH2EdACUlZg35psmd/3xL2duFQ==} @@ -20338,6 +20728,9 @@ packages: vfile@5.3.7: resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + vite-node@1.1.0: resolution: {integrity: sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -23980,13 +24373,13 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} - '@langchain/community@0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(chromadb@1.7.2(openai@4.23.0))(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)': + '@langchain/community@0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(chromadb@1.7.2(openai@4.23.0))(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)': dependencies: - '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) - '@langchain/openai': 0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)) + '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) + '@langchain/openai': 0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)) expr-eval: 2.0.2 flat: 5.0.2 - langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) + langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) uuid: 9.0.1 zod: 3.23.8 zod-to-json-schema: 3.23.1(zod@3.23.8) @@ -24007,13 +24400,13 @@ snapshots: - langchain - openai - '@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0)': + '@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0)': dependencies: ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.12 - langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) + langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) ml-distance: 4.0.1 mustache: 4.2.0 p-queue: 6.6.2 @@ -24025,13 +24418,13 @@ snapshots: - langchain - openai - '@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0)': + '@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0)': dependencies: ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.12 - langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) + langsmith: 0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) ml-distance: 4.0.1 mustache: 4.2.0 p-queue: 6.6.2 @@ -24043,9 +24436,9 @@ snapshots: - langchain - openai - '@langchain/openai@0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))': + '@langchain/openai@0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))': dependencies: - '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) + '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) js-tiktoken: 1.0.12 openai: 4.52.0 zod: 3.23.8 @@ -24541,16 +24934,29 @@ snapshots: dependencies: '@prisma/debug': 5.15.1 + '@radix-ui/colors@3.0.0': {} + '@radix-ui/number@1.0.1': dependencies: '@babel/runtime': 7.24.7 + '@radix-ui/number@1.1.0': {} + '@radix-ui/primitive@1.0.1': dependencies: '@babel/runtime': 7.24.7 '@radix-ui/primitive@1.1.0': {} + '@radix-ui/react-accessible-icon@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-alert-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -24584,6 +24990,15 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-aspect-ratio@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-avatar@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) @@ -24596,6 +25011,22 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-checkbox@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -24634,6 +25065,20 @@ snapshots: optionalDependencies: '@types/react': 18.2.55 + '@radix-ui/react-context-menu@2.2.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-context@1.0.1(@types/react@18.2.55)(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -24760,6 +25205,37 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-form@0.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-label': 2.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-hover-card@1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-id@1.0.1(@types/react@18.2.55)(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -24810,6 +25286,51 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-navigation-menu@1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-popover@1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.7(@types/react@18.2.55)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-popper@1.1.2(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -24906,6 +25427,24 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -24923,6 +25462,23 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-scroll-area@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-select@1.2.2(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -24953,6 +25509,35 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.7(@types/react@18.2.55)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -24962,6 +25547,25 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-slider@1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-slot@1.0.2(@types/react@18.2.55)(react@18.2.0)': dependencies: '@babel/runtime': 7.24.7 @@ -25028,6 +25632,32 @@ snapshots: '@types/react': 18.2.55 '@types/react-dom': 18.3.0 + '@radix-ui/react-toggle-group@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + + '@radix-ui/react-toggle@1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@radix-ui/react-tooltip@1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -25172,6 +25802,49 @@ snapshots: '@radix-ui/rect@1.1.0': {} + '@radix-ui/themes@3.1.3(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/colors': 3.0.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-accessible-icon': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-alert-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-aspect-ratio': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-avatar': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-checkbox': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-context-menu': 2.2.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-dropdown-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-form': 0.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-hover-card': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-navigation-menu': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-popover': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-progress': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-radio-group': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-scroll-area': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-select': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slider': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-switch': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-tabs': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle-group': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-tooltip': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.55)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + classnames: 2.3.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll-bar: 2.3.4(@types/react@18.2.55)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.55 + '@types/react-dom': 18.3.0 + '@react-dnd/asap@4.0.1': {} '@react-dnd/invariant@2.0.0': {} @@ -26487,9 +27160,9 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@tanstack/react-table@8.17.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@tanstack/react-table@8.20.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@tanstack/table-core': 8.17.3 + '@tanstack/table-core': 8.20.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -26504,7 +27177,7 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@tanstack/table-core@8.17.3': {} + '@tanstack/table-core@8.20.1': {} '@tanstack/virtual-core@3.0.0-beta.54': {} @@ -26883,6 +27556,30 @@ snapshots: dependencies: '@types/node': 20.14.8 + '@types/d3-array@3.2.1': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.0': {} + + '@types/d3-scale@4.0.8': + dependencies: + '@types/d3-time': 3.0.3 + + '@types/d3-shape@3.1.6': + dependencies: + '@types/d3-path': 3.1.0 + + '@types/d3-time@3.0.3': {} + + '@types/d3-timer@3.0.2': {} + '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 @@ -27062,6 +27759,10 @@ snapshots: dependencies: '@types/react': 18.2.55 + '@types/react-grid-layout@1.3.5': + dependencies: + '@types/react': 18.2.55 + '@types/react-is@18.3.0': dependencies: '@types/react': 18.2.55 @@ -28274,6 +28975,17 @@ snapshots: dependencies: fill-range: 7.1.1 + broadcast-channel@3.7.0: + dependencies: + '@babel/runtime': 7.24.7 + detect-node: 2.1.0 + js-sha3: 0.8.0 + microseconds: 0.2.0 + nano-time: 1.0.0 + oblivious-set: 1.0.0 + rimraf: 3.0.2 + unload: 2.2.0 + browserify-zlib@0.1.4: dependencies: pako: 0.2.9 @@ -28517,6 +29229,8 @@ snapshots: dependencies: clsx: 2.0.0 + classnames@2.3.2: {} + classnames@2.5.1: {} clean-stack@5.2.0: @@ -29096,6 +29810,44 @@ snapshots: - '@types/node' - typescript + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + d@1.0.2: dependencies: es5-ext: 0.10.64 @@ -29169,6 +29921,8 @@ snapshots: decamelize@1.2.0: {} + decimal.js-light@2.5.1: {} + decimal.js@10.4.3: {} decode-named-character-reference@1.0.2: @@ -29296,6 +30050,8 @@ snapshots: detect-node-es@1.1.0: {} + detect-node@2.1.0: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -30221,6 +30977,8 @@ snapshots: fast-equals@4.0.3: {} + fast-equals@5.0.1: {} + fast-fifo@1.3.2: {} fast-glob@3.3.2: @@ -31129,6 +31887,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + internmap@2.0.3: {} + interpret@3.1.1: {} into-stream@7.0.0: @@ -31508,6 +32268,8 @@ snapshots: js-cookie@3.0.5: {} + js-sha3@0.8.0: {} + js-tiktoken@1.0.12: dependencies: base64-js: 1.5.1 @@ -31781,12 +32543,12 @@ snapshots: kolorist@1.8.0: {} - langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0): + langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0): dependencies: '@anthropic-ai/sdk': 0.9.1 - '@langchain/community': 0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(chromadb@1.7.2(openai@4.23.0))(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) - '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) - '@langchain/openai': 0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)) + '@langchain/community': 0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(chromadb@1.7.2(openai@4.23.0))(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) + '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) + '@langchain/openai': 0.0.34(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0)) binary-extensions: 2.3.0 expr-eval: 2.0.2 js-tiktoken: 1.0.12 @@ -31892,7 +32654,7 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 - langsmith@0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0): + langsmith@0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0): dependencies: '@types/uuid': 9.0.8 commander: 10.0.1 @@ -31900,11 +32662,11 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) - langchain: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) + '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.23.0) + langchain: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) openai: 4.23.0 - langsmith@0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0): + langsmith@0.1.32(@langchain/core@0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0))(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0): dependencies: '@types/uuid': 9.0.8 commander: 10.0.1 @@ -31912,8 +32674,8 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) - langchain: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0))(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) + '@langchain/core': 0.1.63(langchain@0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0))(openai@4.52.0) + langchain: 0.0.209(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-s3@3.600.0)(@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0))(@smithy/util-utf8@2.3.0)(axios@1.7.2)(cheerio@1.0.0-rc.12)(chromadb@1.7.2(openai@4.23.0))(ignore@5.3.1)(ioredis@5.4.1)(jsdom@23.2.0)(jsonwebtoken@9.0.2)(lodash@4.17.21)(openai@4.23.0)(pg@8.12.0)(replicate@0.25.2)(ws@8.18.0) openai: 4.52.0 language-subtag-registry@0.3.23: {} @@ -32175,7 +32937,7 @@ snapshots: dependencies: es5-ext: 0.10.64 - lucide-react@0.265.0(react@18.2.0): + lucide-react@0.435.0(react@18.2.0): dependencies: react: 18.2.0 @@ -32939,6 +33701,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + microseconds@0.2.0: {} + mime-db@1.52.0: {} mime-types@2.1.35: @@ -33158,6 +33922,10 @@ snapshots: nano-pubsub@3.0.0: {} + nano-time@1.0.0: + dependencies: + big-integer: 1.6.52 + nanoid@3.3.6: {} nanoid@3.3.7: {} @@ -33439,6 +34207,8 @@ snapshots: obliterator@2.0.4: {} + oblivious-set@1.0.0: {} + observable-callback@1.0.3(rxjs@7.8.1): dependencies: rxjs: 7.8.1 @@ -34391,6 +35161,13 @@ snapshots: react: 18.2.0 scheduler: 0.23.2 + react-draggable@4.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + clsx: 1.2.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-dropzone@11.7.1(react@18.2.0): dependencies: attr-accept: 2.2.2 @@ -34419,6 +35196,16 @@ snapshots: optionalDependencies: '@types/react': 18.2.55 + react-grid-layout@1.3.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + clsx: 1.2.1 + lodash.isequal: 4.5.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-draggable: 4.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-resizable: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-hook-form@7.52.0(react@18.2.0): dependencies: react: 18.2.0 @@ -34472,6 +35259,15 @@ snapshots: transitivePeerDependencies: - supports-color + react-query@3.39.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@babel/runtime': 7.24.7 + broadcast-channel: 3.7.0 + match-sorter: 6.3.4 + react: 18.2.0 + optionalDependencies: + react-dom: 18.2.0(react@18.2.0) + react-redux@7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.24.7 @@ -34508,6 +35304,14 @@ snapshots: react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.4(@types/react@18.2.55)(react@18.2.0): + dependencies: + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.2.55)(react@18.2.0) + tslib: 2.6.3 + optionalDependencies: + '@types/react': 18.2.55 + react-remove-scroll-bar@2.3.6(@types/react@18.2.55)(react@18.2.0): dependencies: react: 18.2.0 @@ -34548,6 +35352,14 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + react-draggable: 4.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + transitivePeerDependencies: + - react-dom + react-rx@2.1.3(react@18.2.0)(rxjs@7.8.1): dependencies: observable-callback: 1.0.3(rxjs@7.8.1) @@ -34578,6 +35390,14 @@ snapshots: - '@types/react' - supports-color + react-smooth@4.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + fast-equals: 5.0.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-style-proptype@3.2.2: dependencies: prop-types: 15.8.1 @@ -34828,6 +35648,23 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.6.3 + recharts-scale@0.4.5: + dependencies: + decimal.js-light: 2.5.1 + + recharts@2.12.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 16.13.1 + react-smooth: 4.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + rechoir@0.8.0: dependencies: resolve: 1.22.8 @@ -35290,7 +36127,7 @@ snapshots: '@sanity/util': 3.47.1(debug@4.3.5) '@sanity/uuid': 3.0.2 '@sentry/react': 8.11.0(react@18.2.0) - '@tanstack/react-table': 8.17.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@tanstack/react-table': 8.20.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tanstack/react-virtual': 3.0.0-beta.54(react@18.2.0) '@types/react-copy-to-clipboard': 5.0.7 '@types/react-is': 18.3.0 @@ -36879,6 +37716,11 @@ snapshots: universalify@2.0.1: {} + unload@2.2.0: + dependencies: + '@babel/runtime': 7.24.7 + detect-node: 2.1.0 + unzipper@0.11.6: dependencies: big-integer: 1.6.52 @@ -37040,6 +37882,23 @@ snapshots: unist-util-stringify-position: 3.0.3 vfile-message: 3.1.4 + victory-vendor@36.9.2: + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.8 + '@types/d3-shape': 3.1.6 + '@types/d3-time': 3.0.3 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + vite-node@1.1.0(@types/node@20.10.3)(terser@5.31.1): dependencies: cac: 6.7.14 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 16205216b..72110495b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -180,7 +180,7 @@ catalog: lodash-es: ^4.17.21 lottie-react: ^2.4.0 lowlight: ^3.0.0 - lucide-react: ^0.265.0 + lucide-react: ^0.435.0 magic-string: ^0.30.10 match-sorter: ^6.3.4 mdast-util-from-markdown: ^2.0.0