From e51187324717ea01b7abe7e91e262a7790753e34 Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Thu, 6 Jun 2024 22:25:13 +0200 Subject: [PATCH 1/6] stuff --- frontend/src/components/ui/table.tsx | 3 +- .../pages/RequestDetailsPage/RelatedIssue.tsx | 19 ++++++++++++ .../RequestDetailsPage/RequestDetails.tsx | 2 +- .../RequestDetailsPage/RequestDetailsPage.tsx | 2 ++ frontend/src/queries/queries.ts | 31 ++++++++++++++++++- frontend/src/queries/types.ts | 29 +++++++++++++++++ 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssue.tsx diff --git a/frontend/src/components/ui/table.tsx b/frontend/src/components/ui/table.tsx index 74ed60a10..b3468afee 100644 --- a/frontend/src/components/ui/table.tsx +++ b/frontend/src/components/ui/table.tsx @@ -58,7 +58,8 @@ const TableRow = React.forwardRef< Loading...; + } + + if (isLoadingError) { + return
Error loading issues
; + } + + return
{issues?.length}
; +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestDetails.tsx b/frontend/src/pages/RequestDetailsPage/RequestDetails.tsx index 689cef6bd..d8004a037 100644 --- a/frontend/src/pages/RequestDetailsPage/RequestDetails.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestDetails.tsx @@ -429,7 +429,7 @@ const InfoLog = ({ log }: { log: MizuLog }) => { return (
+ {traceId && } All Errors diff --git a/frontend/src/queries/queries.ts b/frontend/src/queries/queries.ts index 6cd8a43c4..f951e09c9 100644 --- a/frontend/src/queries/queries.ts +++ b/frontend/src/queries/queries.ts @@ -1,7 +1,13 @@ -import { QueryClient, QueryClientProvider, useQuery } from "react-query"; +import { + QueryClient, + QueryClientProvider, + QueryFunctionContext, + useQuery, +} from "react-query"; import { objectWithKeyAndValue } from "@/utils"; import { + GitHubIssuesSchema, MizuApiLogResponseSchema, type MizuLog, type MizuRequestEnd, @@ -116,3 +122,26 @@ export function getTraceDescription(trace: MizuTrace) { } return "Unknown trace"; } + +export function useRelevantIssues(traceId: string) { + return useQuery({ + queryKey: ["relevantIssues", traceId], + queryFn: fetchRelevantIssues, + }); +} + +async function fetchRelevantIssues( + context: QueryFunctionContext<[string, string]>, +) { + const traceId = context.queryKey[1]; + try { + const response = await fetch(`/v0/relevant-issues/${traceId}`, { + mode: "cors", + }); + const data = await response.json(); + return GitHubIssuesSchema.parse(data); + // return data as Array; + } catch (e: unknown) { + console.error("Error fetching GitHub issue for a trace: ", e); + } +} diff --git a/frontend/src/queries/types.ts b/frontend/src/queries/types.ts index b04fb6b47..10302e591 100644 --- a/frontend/src/queries/types.ts +++ b/frontend/src/queries/types.ts @@ -122,3 +122,32 @@ export const isKnownMizuMessage = ( export const MizuApiLogResponseSchema = z.object({ logs: z.array(MizuLogSchema), }); + +export const GitHubLabelSchema = z.union([ + z.string(), + z.object({ + id: z.number().optional(), + node_id: z.string().optional(), + url: z.string().optional(), + name: z.string().optional(), + description: z.string().optional(), + color: z.string().optional(), + default: z.boolean().optional(), + }), +]); + +export const GitHubIssueSchema = z.object({ + id: z.number(), + owner: z.string(), + repo: z.string(), + url: z.string(), + title: z.string(), + body: z.string(), + state: z.enum(["open", "closed"]), + labels: z.array(GitHubLabelSchema), + createdAt: z.string(), + updatedAt: z.string(), + closedAt: z.string(), +}); + +export const GitHubIssuesSchema = z.array(GitHubIssueSchema); From 027805d5c50c3a1e584b000603044a9d2753c450 Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Fri, 7 Jun 2024 09:53:36 +0200 Subject: [PATCH 2/6] Eh... --- frontend/package.json | 4 +-- frontend/src/App.tsx | 2 +- frontend/src/components/ui/table.tsx | 1 + .../src/pages/RequestDetailsPage/columns.tsx | 1 - .../{Requests.tsx => RequestsPage.tsx} | 34 ++----------------- .../src/pages/RequestsPage/RequestsTable.tsx | 32 +++++++++++++++++ frontend/src/pages/RequestsPage/columns.tsx | 34 ++++++++++++++++--- frontend/src/queries/queries.ts | 18 +++++++++- frontend/src/queries/types.ts | 3 ++ 9 files changed, 88 insertions(+), 41 deletions(-) rename frontend/src/pages/RequestsPage/{Requests.tsx => RequestsPage.tsx} (87%) create mode 100644 frontend/src/pages/RequestsPage/RequestsTable.tsx diff --git a/frontend/package.json b/frontend/package.json index 6ed5f26d6..e11e91514 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,8 +6,8 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "lint:ci": "biome ci . && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 && tsc", + "lint:ci": "biome ci . && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 && tsc", "format": "biome check . --write", "preview": "vite preview", "deploy": "npm run build && wrangler pages deploy dist" diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7745b88bf..b839ebb9d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,7 @@ import { QueryClientProvider, queryClient } from "@/queries"; import { Route, BrowserRouter as Router, Routes } from "react-router-dom"; import Layout from "./Layout"; import { RequestDetailsPage } from "./pages/RequestDetailsPage/RequestDetailsPage"; -import { RequestsPage } from "./pages/RequestsPage/Requests"; +import { RequestsPage } from "./pages/RequestsPage/RequestsPage"; export function App() { return ( diff --git a/frontend/src/components/ui/table.tsx b/frontend/src/components/ui/table.tsx index b3468afee..23eb541f1 100644 --- a/frontend/src/components/ui/table.tsx +++ b/frontend/src/components/ui/table.tsx @@ -60,6 +60,7 @@ const TableRow = React.forwardRef< className={cn( "border-b transition-colors data-[state=selected]:bg-muted", { "hover:bg-muted/50": props.onClick }, + { "cursor-pointer": props.onClick }, className, )} {...props} diff --git a/frontend/src/pages/RequestDetailsPage/columns.tsx b/frontend/src/pages/RequestDetailsPage/columns.tsx index acb4cf535..029e4184c 100644 --- a/frontend/src/pages/RequestDetailsPage/columns.tsx +++ b/frontend/src/pages/RequestDetailsPage/columns.tsx @@ -10,7 +10,6 @@ import { import type { MizuTrace } from "@/queries"; import { formatDate } from "@/utils"; -// import type { MizuTrace } from "@/queries/decoders" import { TraceDetails } from "./RequestDetails"; // Extend the ColumnMeta type to include headerClassName and cellClassName diff --git a/frontend/src/pages/RequestsPage/Requests.tsx b/frontend/src/pages/RequestsPage/RequestsPage.tsx similarity index 87% rename from frontend/src/pages/RequestsPage/Requests.tsx rename to frontend/src/pages/RequestsPage/RequestsPage.tsx index dc92c8c57..1cfdc2a1e 100644 --- a/frontend/src/pages/RequestsPage/Requests.tsx +++ b/frontend/src/pages/RequestsPage/RequestsPage.tsx @@ -4,9 +4,8 @@ import { TrashIcon, // ListBulletIcon as ListFilter, // FIXME } from "@radix-ui/react-icons"; -import { useEffect, useMemo } from "react"; +import { useEffect } from "react"; import { useQueryClient } from "react-query"; -import { useNavigate } from "react-router-dom"; import { Card, @@ -25,37 +24,10 @@ import { // DropdownMenuTrigger, // } from "@/components/ui/dropdown-menu" -import { DataTable } from "@/components/ui/DataTable"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { type MizuTrace, useMizuTraces } from "@/queries"; -import { columns } from "./columns"; - -type LevelFilter = "all" | "error" | "warning" | "info" | "debug"; - -const RequestsTable = ({ - traces, - filter, -}: { traces: MizuTrace[]; filter: LevelFilter }) => { - const navigate = useNavigate(); - - const filteredTraces = useMemo(() => { - if (filter === "all") { - return traces; - } - return traces.filter((trace) => - trace.logs.some((log) => log.level === filter), - ); - }, [traces, filter]); - - return ( - navigate(`/requests/${row.id}`)} - /> - ); -}; +import { useMizuTraces } from "@/queries"; +import { RequestsTable } from "./RequestsTable"; export function RequestsPage() { const queryClient = useQueryClient(); diff --git a/frontend/src/pages/RequestsPage/RequestsTable.tsx b/frontend/src/pages/RequestsPage/RequestsTable.tsx new file mode 100644 index 000000000..efd5f5448 --- /dev/null +++ b/frontend/src/pages/RequestsPage/RequestsTable.tsx @@ -0,0 +1,32 @@ +import { useMemo } from "react"; +import { useNavigate } from "react-router-dom"; + +import { DataTable } from "@/components/ui/DataTable"; +import type { MizuTrace } from "@/queries"; +import { columns } from "./columns"; + +type LevelFilter = "all" | "error" | "warning" | "info" | "debug"; + +export const RequestsTable = ({ + traces, + filter, +}: { traces: MizuTrace[]; filter: LevelFilter }) => { + const navigate = useNavigate(); + + const filteredTraces = useMemo(() => { + if (filter === "all") { + return traces; + } + return traces.filter((trace) => + trace.logs.some((log) => log.level === filter), + ); + }, [traces, filter]); + + return ( + navigate(`/requests/${row.id}`)} + /> + ); +}; diff --git a/frontend/src/pages/RequestsPage/columns.tsx b/frontend/src/pages/RequestsPage/columns.tsx index 16cd37e77..0946c0a6d 100644 --- a/frontend/src/pages/RequestsPage/columns.tsx +++ b/frontend/src/pages/RequestsPage/columns.tsx @@ -41,11 +41,35 @@ export const columns: ColumnDef[] = [ }, }, { - accessorKey: "description", - header: "Summary", + accessorKey: "method", + header: "Method", + meta: { + headerClassName: "w-[80px]", + cellClassName: "font-mono", + }, + }, + { + accessorKey: "path", + header: "Path", meta: { headerClassName: "", - cellClassName: "font-medium", + cellClassName: "font-mono", + }, + }, + { + accessorKey: "duration", + header: "Duration", + meta: { + headerClassName: "w-[80px]", + cellClassName: "font-mono", + }, + }, + { + accessorKey: "size", + header: "Size", + meta: { + headerClassName: "w-[80px]", + cellClassName: "font-mono", }, }, { @@ -54,7 +78,7 @@ export const columns: ColumnDef[] = [ accessorFn: (row) => formatDate(new Date(row.logs?.[0]?.timestamp)), meta: { // NOTE - This is how to hide a cell depending on breakpoint! - headerClassName: "hidden md:table-cell", + headerClassName: "hidden md:table-cell w-16", cellClassName: "hidden md:table-cell font-mono text-xs", }, }, @@ -62,7 +86,7 @@ export const columns: ColumnDef[] = [ id: "open", cell: (props) => , meta: { - headerClassName: "", + headerClassName: "w-16", cellClassName: "flex items-center space-x-2", }, }), diff --git a/frontend/src/queries/queries.ts b/frontend/src/queries/queries.ts index f951e09c9..452a887f7 100644 --- a/frontend/src/queries/queries.ts +++ b/frontend/src/queries/queries.ts @@ -43,8 +43,11 @@ async function fetchMizuTraces() { id: log.traceId, description: "", status: "", + method: "", + path: "", duration: "", logs: [] as Array, + size: null, }); } const trace = map.get(log.traceId); @@ -78,14 +81,27 @@ async function fetchMizuTraces() { return comparison; }); - trace.duration = "TODO"; + // We're currently not using the description field, so we may want to remove this trace.description = getTraceDescription(trace); const response = trace.logs.find((l) => isMizuRequestEndMessage(l.message), ) as (MizuLog & { message: MizuRequestEnd }) | undefined; + const request = trace.logs.find((l) => + isMizuRequestStartMessage(l.message), + ) as (MizuLog & { message: MizuRequestStart }) | undefined; + const status = response?.message.status; trace.status = typeof status === "string" ? status : "unknown"; + const size = response?.message.body.length; + trace.size = typeof size === "number" ? size : null; + const duration = response?.message.elapsed; + trace.duration = typeof duration === "string" ? duration : "-"; + + const method = request?.message.method; + trace.method = typeof method === "string" ? method : "unknown"; + const path = request?.message.path; + trace.path = typeof path === "string" ? path : "unknown"; traces.push(trace); } diff --git a/frontend/src/queries/types.ts b/frontend/src/queries/types.ts index 10302e591..935448571 100644 --- a/frontend/src/queries/types.ts +++ b/frontend/src/queries/types.ts @@ -78,7 +78,10 @@ const MizuTraceSchema = z.object({ id: z.string(), description: z.string(), status: z.string(), + method: z.string(), + path: z.string(), duration: z.string(), + size: z.number().nonnegative().nullable(), logs: z.array(MizuLogSchema), }); From a2b1a45a5d86c59b2eb952dcfa04783aa26b9b36 Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Fri, 7 Jun 2024 13:59:52 +0200 Subject: [PATCH 3/6] Fix matchingIssues encoding issue --- api/scripts/seed-assets/mizu_logs.json | 492 ++++++++++++++++++++++++- 1 file changed, 491 insertions(+), 1 deletion(-) diff --git a/api/scripts/seed-assets/mizu_logs.json b/api/scripts/seed-assets/mizu_logs.json index 92d055292..2182757b8 100644 --- a/api/scripts/seed-assets/mizu_logs.json +++ b/api/scripts/seed-assets/mizu_logs.json @@ -1 +1,491 @@ -[{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/bugs/unreachable","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:56:10.015Z","traceId":"777f0130-246f-4933-abbc-523ee0bec80d","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:10","updatedAt":"2024-06-04 21:56:10","matchingIssues":null},{"message":"hahaaa","args":[{"name":"NeonDbError","severity":"ERROR","code":"22P02","where":"unnamed portal parameter $1 = '...'","file":"numutils.c","line":"617","routine":"pg_strtoint32_safe"}],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"12508","column":"11","method":"neonDbErrorToJson"},"level":"info","timestamp":"2024-06-04T21:56:10.538Z","traceId":"777f0130-246f-4933-abbc-523ee0bec80d","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:10","updatedAt":"2024-06-04 21:56:10","matchingIssues":null},{"message":"SOURCE","args":[null],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"12509","column":"11","method":"neonDbErrorToJson"},"level":"info","timestamp":"2024-06-04T21:56:10.541Z","traceId":"777f0130-246f-4933-abbc-523ee0bec80d","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:10","updatedAt":"2024-06-04 21:56:10","matchingIssues":null},{"message":{"name":"NeonDbError","message":"invalid input syntax for type integer: \"NaN\""},"args":[],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"6507","column":"11","method":"errorHandler"},"level":"error","timestamp":"2024-06-04T21:56:10.538Z","traceId":"777f0130-246f-4933-abbc-523ee0bec80d","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:10","updatedAt":"2024-06-04 21:56:10","matchingIssues":null},{"message":{"method":"GET","lifecycle":"response","path":"/bugs/unreachable","route":"/bugs/:id","handler":"async (c) => {\n const { id: idString } = c.req.param();\n const id = Number.parseInt(idString, 10);\n const sql2 = zs(c.env.DATABASE_URL);\n const db = drizzle(sql2, { schema: schema_exports });\n const bug = await db.select().from(bugs).where(eq(bugs.id, id));\n return c.json(bug);\n}","handlerType":"handler","status":"500","headers":{"content-type":"text/plain; charset=UTF-8"},"body":"Internal Server Error","elapsed":"526ms"},"args":[[]],"callerLocation":null,"level":"error","timestamp":"2024-06-04T21:56:10.542Z","traceId":"777f0130-246f-4933-abbc-523ee0bec80d","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:10","updatedAt":"2024-06-04 21:56:10","matchingIssues":null},{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/no-db","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:56:46.755Z","traceId":"5d806922-3db5-4872-94e6-8cda9af85ec2","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:46","updatedAt":"2024-06-04 21:56:46","matchingIssues":null},{"message":{"name":"Error","message":"No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?","stack":"Error: No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?\n at neon (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:5477:11)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13062:16\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13012:9\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6069:12"},"args":[],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"6507","column":"11","method":"errorHandler"},"level":"error","timestamp":"2024-06-04T21:56:46.756Z","traceId":"5d806922-3db5-4872-94e6-8cda9af85ec2","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:46","updatedAt":"2024-06-04 21:56:46","matchingIssues":"[2333874415,2329478110]"},{"message":{"method":"GET","lifecycle":"response","path":"/no-db","route":"/no-db","handler":"(c) => {\n const sql2 = zs(c.env.DATABASE_URL_BLANK);\n const db = drizzle(sql2);\n return c.text(\"Hello Hono!\");\n}","handlerType":"handler","status":"500","headers":{"content-type":"text/plain; charset=UTF-8"},"body":"Internal Server Error","elapsed":"1ms"},"args":[[]],"callerLocation":null,"level":"error","timestamp":"2024-06-04T21:56:46.757Z","traceId":"5d806922-3db5-4872-94e6-8cda9af85ec2","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:46","updatedAt":"2024-06-04 21:56:46","matchingIssues":null},{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/no-db","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:56:50.974Z","traceId":"9c4032b0-bf13-4344-b1dd-56e14b4cfc66","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:50","updatedAt":"2024-06-04 21:56:50","matchingIssues":null},{"message":{"name":"Error","message":"No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?","stack":"Error: No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?\n at neon (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:5477:11)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13062:16\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13012:9\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6069:12"},"args":[],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"6507","column":"11","method":"errorHandler"},"level":"error","timestamp":"2024-06-04T21:56:50.974Z","traceId":"9c4032b0-bf13-4344-b1dd-56e14b4cfc66","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:50","updatedAt":"2024-06-04 21:56:50","matchingIssues":"[2333874415]"},{"message":{"method":"GET","lifecycle":"response","path":"/no-db","route":"/no-db","handler":"(c) => {\n const sql2 = zs(c.env.DATABASE_URL_BLANK);\n const db = drizzle(sql2);\n return c.text(\"Hello Hono!\");\n}","handlerType":"handler","status":"500","headers":{"content-type":"text/plain; charset=UTF-8"},"body":"Internal Server Error","elapsed":"1ms"},"args":[[]],"callerLocation":null,"level":"error","timestamp":"2024-06-04T21:56:50.975Z","traceId":"9c4032b0-bf13-4344-b1dd-56e14b4cfc66","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:50","updatedAt":"2024-06-04 21:56:50","matchingIssues":null},{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/jsx-fragments","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:56:55.891Z","traceId":"c3fdcaa8-afb9-4b49-973d-3395b5b99530","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:55","updatedAt":"2024-06-04 21:56:55","matchingIssues":null},{"message":{"name":"TypeError","message":"Cannot read properties of null (reading 'isEscaped')","stack":"TypeError: Cannot read properties of null (reading 'isEscaped')\n at JSXFunctionNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12954:47)\n at childrenToStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12829:13)\n at JSXFragmentNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12963:5)\n at JSXFunctionNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12953:11)\n at JSXFunctionNode.toString (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12864:12)\n at Context.html (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6045:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13082:12\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)"},"args":[],"callerLocation":{"file":"file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js","line":"6507","column":"11","method":"errorHandler"},"level":"error","timestamp":"2024-06-04T21:56:55.891Z","traceId":"c3fdcaa8-afb9-4b49-973d-3395b5b99530","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:55","updatedAt":"2024-06-04 21:56:55","matchingIssues":"[1926127358,2129129474]"},{"message":{"method":"GET","lifecycle":"response","path":"/jsx-fragments","route":"/jsx-fragments","handler":"async (c) => {\n return c.html(/* @__PURE__ */ jsxDEV2(Fragment2, { children: [\n /* @__PURE__ */ jsxDEV2(\"div\", { children: \"FOO\" }),\n /* @__PURE__ */ jsxDEV2(FaultyComp, { if: false, children: /* @__PURE__ */ jsxDEV2(\"div\", { children: \"BAR\" }) })\n ] }));\n}","handlerType":"handler","status":"500","headers":{"content-type":"text/plain; charset=UTF-8"},"body":"Internal Server Error","elapsed":"1ms"},"args":[[]],"callerLocation":null,"level":"error","timestamp":"2024-06-04T21:56:55.892Z","traceId":"c3fdcaa8-afb9-4b49-973d-3395b5b99530","service":null,"ignored":0,"createdAt":"2024-06-04 21:56:55","updatedAt":"2024-06-04 21:56:55","matchingIssues":null},{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:57:02.625Z","traceId":"a2c458fd-7440-4114-b710-052643dc6f62","service":null,"ignored":0,"createdAt":"2024-06-04 21:57:02","updatedAt":"2024-06-04 21:57:02","matchingIssues":null},{"message":{"method":"GET","lifecycle":"response","path":"/","route":"/","handler":"(c) => {\n return c.text(\"Hello Hono!\");\n}","handlerType":"handler","status":"200","headers":{"content-type":"text/plain;charset=UTF-8"},"body":"Hello Hono!","elapsed":"1ms"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:57:02.627Z","traceId":"a2c458fd-7440-4114-b710-052643dc6f62","service":null,"ignored":0,"createdAt":"2024-06-04 21:57:02","updatedAt":"2024-06-04 21:57:02","matchingIssues":null},{"message":{"lifecycle":"request","method":"GET","headers":{"accept":"*/*","accept-encoding":"br, gzip","host":"localhost:8787","user-agent":"curl/8.4.0"},"path":"/","env":{"MIZU_ENDPOINT":"http://localhost:8788/v0/logs","DATABASE_URL":"postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require"},"params":{},"file":"/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:57:05.986Z","traceId":"a2de6a1f-6b16-4144-a659-868c58d8f4ac","service":null,"ignored":0,"createdAt":"2024-06-04 21:57:05","updatedAt":"2024-06-04 21:57:05","matchingIssues":null},{"message":{"method":"GET","lifecycle":"response","path":"/","route":"/","handler":"(c) => {\n return c.text(\"Hello Hono!\");\n}","handlerType":"handler","status":"200","headers":{"content-type":"text/plain;charset=UTF-8"},"body":"Hello Hono!","elapsed":"1ms"},"args":[[]],"callerLocation":null,"level":"info","timestamp":"2024-06-04T21:57:05.987Z","traceId":"a2de6a1f-6b16-4144-a659-868c58d8f4ac","service":null,"ignored":0,"createdAt":"2024-06-04 21:57:05","updatedAt":"2024-06-04 21:57:05","matchingIssues":null}] \ No newline at end of file +[ + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/bugs/unreachable", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:56:10.015Z", + "traceId": "777f0130-246f-4933-abbc-523ee0bec80d", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:10", + "updatedAt": "2024-06-04 21:56:10", + "matchingIssues": null + }, + { + "message": "hahaaa", + "args": [ + { + "name": "NeonDbError", + "severity": "ERROR", + "code": "22P02", + "where": "unnamed portal parameter $1 = '...'", + "file": "numutils.c", + "line": "617", + "routine": "pg_strtoint32_safe" + } + ], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "12508", + "column": "11", + "method": "neonDbErrorToJson" + }, + "level": "info", + "timestamp": "2024-06-04T21:56:10.538Z", + "traceId": "777f0130-246f-4933-abbc-523ee0bec80d", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:10", + "updatedAt": "2024-06-04 21:56:10", + "matchingIssues": null + }, + { + "message": "SOURCE", + "args": [ + null + ], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "12509", + "column": "11", + "method": "neonDbErrorToJson" + }, + "level": "info", + "timestamp": "2024-06-04T21:56:10.541Z", + "traceId": "777f0130-246f-4933-abbc-523ee0bec80d", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:10", + "updatedAt": "2024-06-04 21:56:10", + "matchingIssues": null + }, + { + "message": { + "name": "NeonDbError", + "message": "invalid input syntax for type integer: \"NaN\"" + }, + "args": [], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "6507", + "column": "11", + "method": "errorHandler" + }, + "level": "error", + "timestamp": "2024-06-04T21:56:10.538Z", + "traceId": "777f0130-246f-4933-abbc-523ee0bec80d", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:10", + "updatedAt": "2024-06-04 21:56:10", + "matchingIssues": null + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/bugs/unreachable", + "route": "/bugs/:id", + "handler": "async (c) => {\n const { id: idString } = c.req.param();\n const id = Number.parseInt(idString, 10);\n const sql2 = zs(c.env.DATABASE_URL);\n const db = drizzle(sql2, { schema: schema_exports });\n const bug = await db.select().from(bugs).where(eq(bugs.id, id));\n return c.json(bug);\n}", + "handlerType": "handler", + "status": "500", + "headers": { + "content-type": "text/plain; charset=UTF-8" + }, + "body": "Internal Server Error", + "elapsed": "526ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "error", + "timestamp": "2024-06-04T21:56:10.542Z", + "traceId": "777f0130-246f-4933-abbc-523ee0bec80d", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:10", + "updatedAt": "2024-06-04 21:56:10", + "matchingIssues": null + }, + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/no-db", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:56:46.755Z", + "traceId": "5d806922-3db5-4872-94e6-8cda9af85ec2", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:46", + "updatedAt": "2024-06-04 21:56:46", + "matchingIssues": null + }, + { + "message": { + "name": "Error", + "message": "No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?", + "stack": "Error: No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?\n at neon (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:5477:11)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13062:16\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13012:9\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6069:12" + }, + "args": [], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "6507", + "column": "11", + "method": "errorHandler" + }, + "level": "error", + "timestamp": "2024-06-04T21:56:46.756Z", + "traceId": "5d806922-3db5-4872-94e6-8cda9af85ec2", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:46", + "updatedAt": "2024-06-04 21:56:46", + "matchingIssues": [2333874415,2329478110] + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/no-db", + "route": "/no-db", + "handler": "(c) => {\n const sql2 = zs(c.env.DATABASE_URL_BLANK);\n const db = drizzle(sql2);\n return c.text(\"Hello Hono!\");\n}", + "handlerType": "handler", + "status": "500", + "headers": { + "content-type": "text/plain; charset=UTF-8" + }, + "body": "Internal Server Error", + "elapsed": "1ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "error", + "timestamp": "2024-06-04T21:56:46.757Z", + "traceId": "5d806922-3db5-4872-94e6-8cda9af85ec2", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:46", + "updatedAt": "2024-06-04 21:56:46", + "matchingIssues": null + }, + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/no-db", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:56:50.974Z", + "traceId": "9c4032b0-bf13-4344-b1dd-56e14b4cfc66", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:50", + "updatedAt": "2024-06-04 21:56:50", + "matchingIssues": null + }, + { + "message": { + "name": "Error", + "message": "No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?", + "stack": "Error: No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?\n at neon (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:5477:11)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13062:16\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13012:9\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6069:12" + }, + "args": [], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "6507", + "column": "11", + "method": "errorHandler" + }, + "level": "error", + "timestamp": "2024-06-04T21:56:50.974Z", + "traceId": "9c4032b0-bf13-4344-b1dd-56e14b4cfc66", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:50", + "updatedAt": "2024-06-04 21:56:50", + "matchingIssues": [2333874415] + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/no-db", + "route": "/no-db", + "handler": "(c) => {\n const sql2 = zs(c.env.DATABASE_URL_BLANK);\n const db = drizzle(sql2);\n return c.text(\"Hello Hono!\");\n}", + "handlerType": "handler", + "status": "500", + "headers": { + "content-type": "text/plain; charset=UTF-8" + }, + "body": "Internal Server Error", + "elapsed": "1ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "error", + "timestamp": "2024-06-04T21:56:50.975Z", + "traceId": "9c4032b0-bf13-4344-b1dd-56e14b4cfc66", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:50", + "updatedAt": "2024-06-04 21:56:50", + "matchingIssues": null + }, + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/jsx-fragments", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:56:55.891Z", + "traceId": "c3fdcaa8-afb9-4b49-973d-3395b5b99530", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:55", + "updatedAt": "2024-06-04 21:56:55", + "matchingIssues": null + }, + { + "message": { + "name": "TypeError", + "message": "Cannot read properties of null (reading 'isEscaped')", + "stack": "TypeError: Cannot read properties of null (reading 'isEscaped')\n at JSXFunctionNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12954:47)\n at childrenToStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12829:13)\n at JSXFragmentNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12963:5)\n at JSXFunctionNode.toStringToBuffer (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12953:11)\n at JSXFunctionNode.toString (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12864:12)\n at Context.html (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6045:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:13082:12\n at dispatch (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6092:23)\n at file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:6093:20\n at logger2 (file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js:12700:11)" + }, + "args": [], + "callerLocation": { + "file": "file:///Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js", + "line": "6507", + "column": "11", + "method": "errorHandler" + }, + "level": "error", + "timestamp": "2024-06-04T21:56:55.891Z", + "traceId": "c3fdcaa8-afb9-4b49-973d-3395b5b99530", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:55", + "updatedAt": "2024-06-04 21:56:55", + "matchingIssues": [1926127358,2129129474] + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/jsx-fragments", + "route": "/jsx-fragments", + "handler": "async (c) => {\n return c.html(/* @__PURE__ */ jsxDEV2(Fragment2, { children: [\n /* @__PURE__ */ jsxDEV2(\"div\", { children: \"FOO\" }),\n /* @__PURE__ */ jsxDEV2(FaultyComp, { if: false, children: /* @__PURE__ */ jsxDEV2(\"div\", { children: \"BAR\" }) })\n ] }));\n}", + "handlerType": "handler", + "status": "500", + "headers": { + "content-type": "text/plain; charset=UTF-8" + }, + "body": "Internal Server Error", + "elapsed": "1ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "error", + "timestamp": "2024-06-04T21:56:55.892Z", + "traceId": "c3fdcaa8-afb9-4b49-973d-3395b5b99530", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:56:55", + "updatedAt": "2024-06-04 21:56:55", + "matchingIssues": null + }, + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:57:02.625Z", + "traceId": "a2c458fd-7440-4114-b710-052643dc6f62", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:57:02", + "updatedAt": "2024-06-04 21:57:02", + "matchingIssues": null + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/", + "route": "/", + "handler": "(c) => {\n return c.text(\"Hello Hono!\");\n}", + "handlerType": "handler", + "status": "200", + "headers": { + "content-type": "text/plain;charset=UTF-8" + }, + "body": "Hello Hono!", + "elapsed": "1ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:57:02.627Z", + "traceId": "a2c458fd-7440-4114-b710-052643dc6f62", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:57:02", + "updatedAt": "2024-06-04 21:57:02", + "matchingIssues": null + }, + { + "message": { + "lifecycle": "request", + "method": "GET", + "headers": { + "accept": "*/*", + "accept-encoding": "br, gzip", + "host": "localhost:8787", + "user-agent": "curl/8.4.0" + }, + "path": "/", + "env": { + "MIZU_ENDPOINT": "http://localhost:8788/v0/logs", + "DATABASE_URL": "postgresql://bugz_owner:FH70RLTDmixu@ep-spring-glitter-a2vyz3ob.eu-central-1.aws.neon.tech/bugz?sslmode=require" + }, + "params": {}, + "file": "/Users/laurynas-fp/Code/work/brettimus-bugs/.wrangler/tmp/dev-cgvDB1/index.js" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:57:05.986Z", + "traceId": "a2de6a1f-6b16-4144-a659-868c58d8f4ac", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:57:05", + "updatedAt": "2024-06-04 21:57:05", + "matchingIssues": null + }, + { + "message": { + "method": "GET", + "lifecycle": "response", + "path": "/", + "route": "/", + "handler": "(c) => {\n return c.text(\"Hello Hono!\");\n}", + "handlerType": "handler", + "status": "200", + "headers": { + "content-type": "text/plain;charset=UTF-8" + }, + "body": "Hello Hono!", + "elapsed": "1ms" + }, + "args": [ + [] + ], + "callerLocation": null, + "level": "info", + "timestamp": "2024-06-04T21:57:05.987Z", + "traceId": "a2de6a1f-6b16-4144-a659-868c58d8f4ac", + "service": null, + "ignored": 0, + "createdAt": "2024-06-04 21:57:05", + "updatedAt": "2024-06-04 21:57:05", + "matchingIssues": null + } +] From baef0588ca60d23f4f88e40d1e2740590bf07939 Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Tue, 11 Jun 2024 13:57:29 +0200 Subject: [PATCH 4/6] Add issues tab --- frontend/package.json | 6 + frontend/src/hooks/index.ts | 1 + frontend/src/hooks/useTimeAgo.ts | 124 +++ .../pages/RequestDetailsPage/IssueIcon.svg | 4 + .../RequestDetailsPage/PullRequestIcon.svg | 3 + .../pages/RequestDetailsPage/RelatedIssue.tsx | 19 - .../RequestDetailsPage/RelatedIssueCard.tsx | 195 ++++ .../RelatedIssueContent.tsx | 37 + .../RelatedIssueCounter.tsx | 17 + .../RequestDetailsPage/RequestDetailsPage.tsx | 16 +- .../src/pages/RequestDetailsPage/TimeAgo.tsx | 12 + frontend/src/queries/types.ts | 2 +- frontend/vite.config.ts | 3 +- package-lock.json | 915 ++++++++++++++++-- 14 files changed, 1270 insertions(+), 84 deletions(-) create mode 100644 frontend/src/hooks/index.ts create mode 100644 frontend/src/hooks/useTimeAgo.ts create mode 100644 frontend/src/pages/RequestDetailsPage/IssueIcon.svg create mode 100644 frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg delete mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssue.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/TimeAgo.tsx diff --git a/frontend/package.json b/frontend/package.json index e11e91514..c0165615a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,22 +23,28 @@ "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-table": "^8.17.3", + "@types/highlight-words-core": "^1.2.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "highlight-words-core": "^1.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-highlight-words": "^0.20.0", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.23.1", + "stopword": "^3.0.1", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", + "vite-plugin-svgr": "^4.2.0", "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20.12.12", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", + "@types/stopword": "^2.0.3", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.5.0", diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts new file mode 100644 index 000000000..1a1d7feab --- /dev/null +++ b/frontend/src/hooks/index.ts @@ -0,0 +1 @@ +export { useTimeAgo} from "./useTimeAgo.js" diff --git a/frontend/src/hooks/useTimeAgo.ts b/frontend/src/hooks/useTimeAgo.ts new file mode 100644 index 000000000..79ed56766 --- /dev/null +++ b/frontend/src/hooks/useTimeAgo.ts @@ -0,0 +1,124 @@ +import calculateDifferenceInMilliseconds from "date-fns/differenceInMilliseconds"; +import format from "date-fns/format"; +import formatDistanceToNow from "date-fns/formatDistanceToNow"; +import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict"; +import { useEffect, useRef, useState } from "react"; + +// In ms +const FIVE_SECONDS = 5000; +const TEN_SECONDS = 10_000; +const HALF_MINUTE = 30_000; +const MINUTE = 60_000; /* 60 * 1000ms */ +const HOUR = 3_600_000; /* 60 * 60 * 1000ms */ +const DAY = 86_400_000; /* 24 hours, 24 * 60 * 60 * 1000ms */ + +// By defining the intervals this way, we can determine which interval should +// be applied in a time window. This allows us to give more descriptive readable +// time strings as defined in the `date-fns` documentation: +// https://date-fns.org/v2.29.2/docs/formatDistanceToNow +const INTERVALS = { + [FIVE_SECONDS]: { + interval: FIVE_SECONDS, + nextInterval: HALF_MINUTE, + }, + [HALF_MINUTE]: { + interval: TEN_SECONDS, + nextInterval: MINUTE, + }, + [MINUTE]: { + interval: MINUTE, + nextInterval: HOUR, + }, + [HOUR]: { + interval: HOUR, + nextInterval: DAY, + }, +} as const; + +/** + * Hook that parses the date string into a human-readable string that updates + * based on the right interval. If the comment is submitted >= 24hrs ago a + * string with that date will be returned that won't be updated. + * + * if the strict option is set to true the helpers like 'almost', 'over', 'less than' and the like + * aren't used. + */ +export function useTimeAgo( + date: string | undefined, + options?: { strict: boolean }, +) { + const { strict = false } = options || {}; + + const interval = useRef<(typeof INTERVALS)[keyof typeof INTERVALS]>( + INTERVALS[FIVE_SECONDS], + ); + const [formattedDate, setFormattedDate] = useState(); + + useEffect(() => { + if (!date) { + return; + } + + const parsedDate = new Date(date); + + const getDifferenceInMilliseconds = () => + calculateDifferenceInMilliseconds(new Date(), parsedDate); + + const setFormatDate = () => { + const formatter = strict + ? formatDistanceToNowStrict + : formatDistanceToNow; + const formattedDate = formatter(parsedDate, { + addSuffix: true, + includeSeconds: true, + }); + setFormattedDate(formattedDate); + }; + + const setConstantDate = () => { + const constantDate = format(parsedDate, "LLLL d, yyyy"); + setFormattedDate(constantDate); + }; + + // Prevent calling the timeout when we know >= 24hrs has passed & set a + // formatted post date string. + if (getDifferenceInMilliseconds() >= DAY) { + setConstantDate(); + return; + } + + let formatTimeout: ReturnType; + const setFormatTimeout = () => { + formatTimeout = setTimeout(() => { + const differenceInMilliSeconds = getDifferenceInMilliseconds(); + // Prevent calling a new timeout when we know >= 24hrs has passed & set + // a formatted post date string. + if (differenceInMilliSeconds >= DAY) { + setConstantDate(); + return; + } + + if (differenceInMilliSeconds >= interval.current.nextInterval) { + if (interval.current.nextInterval === DAY) { + setConstantDate(); + return; + } + + interval.current = INTERVALS[interval.current.nextInterval]; + } + + setFormatDate(); + setFormatTimeout(); + }, interval.current.interval); + }; + + setFormatDate(); + setFormatTimeout(); + + return () => { + clearTimeout(formatTimeout); + }; + }, [date, strict]); + + return formattedDate; +} diff --git a/frontend/src/pages/RequestDetailsPage/IssueIcon.svg b/frontend/src/pages/RequestDetailsPage/IssueIcon.svg new file mode 100644 index 000000000..862cb289c --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/IssueIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg b/frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg new file mode 100644 index 000000000..ea43601d6 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssue.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssue.tsx deleted file mode 100644 index 3b6be13b3..000000000 --- a/frontend/src/pages/RequestDetailsPage/RelatedIssue.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useRelevantIssues } from "@/queries/queries"; - -export function RelatedIssue({ traceId }: { traceId: string }) { - const { - data: issues, - isLoading, - isLoadingError, - } = useRelevantIssues(traceId); - - if (isLoading) { - return
Loading...
; - } - - if (isLoadingError) { - return
Error loading issues
; - } - - return
{issues?.length}
; -} diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx new file mode 100644 index 000000000..4548902c8 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx @@ -0,0 +1,195 @@ +import { GithubIssue } from "@/queries/types"; +import IssueIcon from "./IssueIcon.svg?react"; +import { TimeAgo } from "./TimeAgo"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; +// import Highlighter from "react-highlight-words"; +import { Chunk, findAll } from "highlight-words-core"; +import { removeStopwords, eng } from "stopword" + +// import PullRequestIcon from "@radix-ui/react-icons/dist/PullRequestIcon"; +import PullRequestIcon from "./PullRequestIcon.svg?react"; + +// const insignificantWords = new Set([ +// "a", "an", "and", "are", "as", "has", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with", "set" +// ]); + + +function replaceNonWordsWithSpace(text: string) { + // Regular expression to match all non-word and non-number characters using Unicode property escapes + const regex = /[^\p{L}\p{N}]/gu; + + // Replace matched characters with a space + let result = text.replace(regex, ' '); + + // Collapse multiple spaces into a single space + result = result.replace(/\s+/g, ' '); + + return result.trim(); // Trim leading/trailing spaces +} + + +function getSignificantWords(query: string) { + const words = replaceNonWordsWithSpace(query).split(/\s+/); + return removeStopwords(words, eng); +} +const searchWords = getSignificantWords("No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?"); + +const sanitize = (text: string) => text; + +const findChunks = ({ + autoEscape, + caseSensitive, + // sanitize = sanitize, + searchWords, + textToHighlight +}: { + autoEscape?: boolean, + caseSensitive?: boolean, + // sanitize?: typeof defaultSanitize, + searchWords: Array, + textToHighlight: string, +}): Array => { + textToHighlight = sanitize(textToHighlight) + console.log('text', textToHighlight) + return searchWords + .filter(searchWord => searchWord) // Remove empty words + .reduce((chunks, searchWord) => { + searchWord = sanitize(searchWord) + + if (autoEscape) { + searchWord = escapeRegExpFn(searchWord) + } + + const regex = new RegExp(`\\b${searchWord}\\b`, caseSensitive ? 'g' : 'gi') + console.log('regex', regex); + let match + while ((match = regex.exec(textToHighlight))) { + let start = match.index + let end = regex.lastIndex + // We do not return zero-length matches + if (end > start) { + chunks.push({ highlight: false, start, end }) + } + + // Prevent browsers like Firefox from getting stuck in an infinite loop + // See http://www.regexguru.com/2008/04/watch-out-for-zero-length-matches/ + if (match.index === regex.lastIndex) { + regex.lastIndex++ + } + } + + return chunks + }, [] as Array) +} + +function escapeRegExpFn(string: string): string { + return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') +} + + +export function RelatedIssueCard(issue: GithubIssue) { + console.log('------------------------------------') + console.log('words', searchWords); + console.log('title', issue.title); + console.log('body', issue.body); + console.log('------------------------------------') + + const chunks = findAll({ + searchWords: [...searchWords], + textToHighlight: issue.body, + findChunks, + }); + + // console.log('chunks', chunks) + return ( +
+
+
+ + + {issue.title} +
+
+ + {issue.owner}/{issue.repo} +
+
+
+ {chunks.map((chunk, index) => { + if (chunk.highlight === false) { + return null; + } + const text = getSurroundingWords(issue.body, chunk.start, chunk.end); + + return {text}{index < chunks.length - 1 ? "..." : ""} ; + })} +
+
+
+
+ Created: +
+ {issue.updatedAt && ( + issue.updatedAt !== issue.createdAt || issue.updatedAt !== issue.closedAt + ) &&
+ Updated: +
} + {issue.closedAt &&
+ Closed: +
} +
+
+
+ ); + +} + + + +function RelatedIcon(props: { isPr: boolean; isClosed: boolean }) { + const { isPr, isClosed } = props; + + const className = isClosed ? "text-purple-500" : "text-green-400"; + + if (isPr) { + return ; + } + + return +} + + +function getSurroundingWords(text: string, startIndex: number, endIndex: number) { + // Ensure the indices are within the bounds of the text + if (startIndex < 0 || endIndex > text.length || startIndex > endIndex) { + throw new Error('Invalid start or end index'); + } + + // Get the text before, the search term, and the text after + const before = text.substring(0, startIndex); + const match = text.substring(startIndex, endIndex); + const after = text.substring(endIndex); + + // Find the last space before the search term in the before text + const beforeLastSpaceIndex = before.lastIndexOf(' '); + + // Find the first space after the search term in the after text + const afterFirstSpaceIndex = after.indexOf(' '); + + // Extract the word before the search term + const wordBefore = beforeLastSpaceIndex !== -1 + ? before.substring(beforeLastSpaceIndex + 1) + : before; + + // Extract the word after the search term + const wordAfter = afterFirstSpaceIndex !== -1 + ? after.substring(0, afterFirstSpaceIndex) + : after; + + // Combine the word before, the match, and the word after + return <> + {wordBefore} + {match} + {wordAfter} + ; +} diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx new file mode 100644 index 000000000..eda43a83b --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx @@ -0,0 +1,37 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { useRelevantIssues } from "@/queries/queries"; +import { RelatedIssueCard } from "./RelatedIssueCard"; + +export function RelatedIssueContent(props: { traceId: string }) { + const { traceId } = props; + const { data, isLoading } = useRelevantIssues(traceId); + + if (isLoading) { + return
Loading...
; + } + if (!data) { + return
No data
+ } + + return ( + + + Related Issues + + See issues related to this request + + + +
    + {data.map((issue) => ( +
  • + + + +
  • + ))} +
+
+
+ ); +} diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx new file mode 100644 index 000000000..6442a6fcf --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx @@ -0,0 +1,17 @@ +import { useRelevantIssues } from "@/queries/queries"; + +export function RelatedIssueCounter({ traceId }: { traceId: string }) { + const { + data: issues, + isLoading, + isLoadingError, + } = useRelevantIssues(traceId); + + if (isLoading || isLoadingError) { + return null; + } + + // console.log("issues", issues); + return issues && issues.length || null + // return
issues.length: {issues?.length}
; +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx b/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx index a625f7886..be9e2e366 100644 --- a/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx @@ -14,8 +14,9 @@ import { useMizuTraces } from "@/queries"; import { isError } from "react-query"; import { useParams } from "react-router-dom"; -import { RelatedIssue } from "./RelatedIssue"; +import { RelatedIssueCounter } from "./RelatedIssueCounter"; import { TraceDetails } from "./RequestDetails"; +import { RelatedIssueContent } from "./RelatedIssueContent"; function useRequestDetails(traceId?: string) { const query = useMizuTraces(); @@ -34,10 +35,16 @@ export function RequestDetailsPage() { return (
- {traceId && } All Errors + Related issues + {traceId && +
+ +
+ } +
{/* TODO - Add global table action buttons? */} @@ -90,6 +97,9 @@ export function RequestDetailsPage() { - + + {traceId && } + + ); } diff --git a/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx b/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx new file mode 100644 index 000000000..14f758e8c --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx @@ -0,0 +1,12 @@ + +import { useTimeAgo } from "../../hooks"; + +export function TimeAgo(props: { date: string }) { + const time = useTimeAgo(props.date); + + if (!time) { + return null; + } + + return {time} +} diff --git a/frontend/src/queries/types.ts b/frontend/src/queries/types.ts index 54f4573de..089c3b2b8 100644 --- a/frontend/src/queries/types.ts +++ b/frontend/src/queries/types.ts @@ -279,7 +279,7 @@ export const GitHubIssueSchema = z.object({ labels: z.array(GitHubLabelSchema), createdAt: z.string(), updatedAt: z.string(), - closedAt: z.string(), + closedAt: z.string().nullable(), }); export const GitHubIssuesSchema = z.array(GitHubIssueSchema); diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index bde4f1827..d86e6af54 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,9 +1,10 @@ import path from "node:path"; import react from "@vitejs/plugin-react-swc"; +import svgr from "vite-plugin-svgr"; import { defineConfig } from "vite"; export default defineConfig({ - plugins: [react()], + plugins: [react(), svgr()], resolve: { alias: { "@": path.resolve(__dirname, "./src"), diff --git a/package-lock.json b/package-lock.json index f40553280..31c0bacb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ }, "api": { "name": "@mizu-dev/studio", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT or Apache 2", "dependencies": { "@hono/node-server": "^1.11.1", @@ -84,22 +84,28 @@ "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-table": "^8.17.3", + "@types/highlight-words-core": "^1.2.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "highlight-words-core": "^1.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-highlight-words": "^0.20.0", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.23.1", + "stopword": "^3.0.1", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", + "vite-plugin-svgr": "^4.2.0", "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20.12.12", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", + "@types/stopword": "^2.0.3", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "@vitejs/plugin-react-swc": "^3.5.0", @@ -130,6 +136,337 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "dependencies": { + "@babel/types": "^7.24.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "dependencies": { + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", @@ -141,6 +478,60 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@biomejs/biome": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.8.0.tgz", @@ -2850,6 +3241,32 @@ "node": ">=14.0.0" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", @@ -2857,7 +3274,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -2870,7 +3286,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -2883,7 +3298,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -2896,7 +3310,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -2909,7 +3322,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2922,7 +3334,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2935,7 +3346,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2948,7 +3358,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2961,7 +3370,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2974,7 +3382,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -2987,7 +3394,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3000,7 +3406,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3013,7 +3418,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -3026,7 +3430,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -3039,7 +3442,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -3052,7 +3454,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -3070,6 +3471,207 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, "node_modules/@swc/cli": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.3.12.tgz", @@ -3430,6 +4032,11 @@ "@types/unist": "*" } }, + "node_modules/@types/highlight-words-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.3.tgz", + "integrity": "sha512-PWNU/NR0CaYEsK38mcCTyDzeS2TlEGK9kRhRMz1i86jVAe836ZlA3gl6QYpu+CG6IpfNKTgWpEnJuRededvC0g==" + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -3526,6 +4133,12 @@ "@types/node": "*" } }, + "node_modules/@types/stopword": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stopword/-/stopword-2.0.3.tgz", + "integrity": "sha512-hioMj0lOvISM+EDevf7ijG8EMbU+J3pj4SstCyfQC1t39uPYpAe7beSfBdU6c1d9jeECTQQtR3UJWtVoUO8Weg==", + "dev": true + }, "node_modules/@types/unist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", @@ -3967,8 +4580,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-hidden": { "version": "1.2.4", @@ -4334,7 +4946,6 @@ "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4437,11 +5048,21 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -4454,7 +5075,6 @@ "version": "1.0.30001627", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4831,6 +5451,11 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -4840,6 +5465,31 @@ "node": ">= 0.6" } }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5069,6 +5719,15 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -5259,8 +5918,7 @@ "node_modules/electron-to-chromium": { "version": "1.4.789", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.789.tgz", - "integrity": "sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==", - "dev": true + "integrity": "sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -5276,6 +5934,17 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", @@ -5287,6 +5956,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/es5-ext": { "version": "0.10.64", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", @@ -5387,7 +6069,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, "engines": { "node": ">=6" } @@ -6236,6 +6917,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-nonce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", @@ -6483,6 +7172,11 @@ "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" }, + "node_modules/highlight-words-core": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.2.tgz", + "integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg==" + }, "node_modules/hono": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/hono/-/hono-4.4.3.tgz", @@ -6590,7 +7284,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6992,7 +7685,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -7000,6 +7692,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7022,6 +7725,11 @@ "node": "*" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7034,6 +7742,17 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -7178,6 +7897,14 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -7366,6 +8093,11 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/memoize-one": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz", + "integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==" + }, "node_modules/memoizee": { "version": "0.4.17", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", @@ -8023,6 +8755,15 @@ "node-gyp-build": "^4.2.2" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -8091,8 +8832,7 @@ "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -8367,7 +9107,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8399,6 +9138,23 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/password-prompt": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.3.tgz", @@ -8464,7 +9220,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -8786,6 +9541,16 @@ "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", "dev": true }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", @@ -8874,6 +9639,24 @@ "react": "^18.3.1" } }, + "node_modules/react-highlight-words": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/react-highlight-words/-/react-highlight-words-0.20.0.tgz", + "integrity": "sha512-asCxy+jCehDVhusNmCBoxDf2mm1AJ//D+EzDx1m5K7EqsMBIHdZ5G4LdwbSEXqZq1Ros0G0UySWmAtntSph7XA==", + "dependencies": { + "highlight-words-core": "^1.2.0", + "memoize-one": "^4.0.0", + "prop-types": "^15.5.8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", @@ -9146,7 +9929,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -9246,7 +10028,6 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -9498,6 +10279,15 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -9597,6 +10387,11 @@ "npm": ">=6" } }, + "node_modules/stopword": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/stopword/-/stopword-3.0.1.tgz", + "integrity": "sha512-pz2XNVtoApA7eSX1viol5ybGGG2Re2bI7/+8TfeaMb09C9NEWgabr7HHf4DbqgrmEuI62sH2BcAEHVAlreCmrQ==" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9855,6 +10650,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, "node_modules/tailwind-merge": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz", @@ -9994,6 +10794,14 @@ "node": ">=0.6.0" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10597,7 +11405,7 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10760,7 +11568,6 @@ "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10872,7 +11679,6 @@ "version": "5.2.12", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", - "dev": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -10923,6 +11729,19 @@ } } }, + "node_modules/vite-plugin-svgr": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", + "integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==", + "dependencies": { + "@rollup/pluginutils": "^5.0.5", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": "^2.6.0 || 3 || 4 || 5" + } + }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -10930,7 +11749,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -10946,7 +11764,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -10962,7 +11779,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -10978,7 +11794,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -10994,7 +11809,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -11010,7 +11824,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -11026,7 +11839,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -11042,7 +11854,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -11058,7 +11869,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11074,7 +11884,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11090,7 +11899,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11106,7 +11914,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11122,7 +11929,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11138,7 +11944,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11154,7 +11959,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11170,7 +11974,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11186,7 +11989,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -11202,7 +12004,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -11218,7 +12019,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -11234,7 +12034,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -11250,7 +12049,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -11266,7 +12064,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -11282,7 +12079,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -11295,7 +12091,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" From 034b88fa5881e0b4af9b6be0db09a6d4928cb83e Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Wed, 12 Jun 2024 13:00:36 +0200 Subject: [PATCH 5/6] Add experimental issue section Preparing for the PR --- frontend/package.json | 2 +- frontend/src/hooks/index.ts | 3 +- frontend/src/hooks/useRequestDetails.ts | 12 ++ .../pages/RequestDetailsPage/DataTable.tsx | 118 ----------- .../RequestDetailsPage/RelatedIssueCard.tsx | 195 ------------------ .../RelatedIssueContent.tsx | 37 ---- .../RequestDetailsPage/RequestDetailsPage.tsx | 29 +-- .../RelatedIssueCounter.tsx | 4 +- .../RelatedIssuesContent/DependenciesList.tsx | 36 ++++ .../RelatedIssuesContent/IssueSection.tsx | 23 +++ .../RelatedIssueCard}/IssueIcon.svg | 0 .../RelatedIssueCard}/PullRequestIcon.svg | 0 .../RelatedIssueCard/RelatedIcon.tsx | 15 ++ .../RelatedIssueCard/RelatedIssueCard.tsx | 89 ++++++++ .../RelatedIssueCard/index.tsx | 1 + .../RelatedIssueCard/utils.tsx | 160 ++++++++++++++ .../RelatedIssuesContent/RelatedIssueList.tsx | 76 +++++++ .../RelatedIssuesContent.tsx | 29 +++ .../RelatedIssuesContent/index.ts | 1 + .../RelatedIssuesContent/utils.ts | 19 ++ .../RequestDetailsPage/RequestIssues/index.ts | 2 + .../src/pages/RequestDetailsPage/TimeAgo.tsx | 3 +- frontend/src/queries/queries.ts | 21 ++ frontend/src/queries/types.ts | 17 ++ frontend/src/vite-env-override.d.ts | 8 + frontend/src/vite-env.d.ts | 1 - frontend/tsconfig.node.json | 3 +- frontend/vite.config.ts | 10 +- package-lock.json | 26 ++- 29 files changed, 549 insertions(+), 391 deletions(-) create mode 100644 frontend/src/hooks/useRequestDetails.ts delete mode 100644 frontend/src/pages/RequestDetailsPage/DataTable.tsx delete mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx delete mode 100644 frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx rename frontend/src/pages/RequestDetailsPage/{ => RequestIssues}/RelatedIssueCounter.tsx (68%) create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/DependenciesList.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/IssueSection.tsx rename frontend/src/pages/RequestDetailsPage/{ => RequestIssues/RelatedIssuesContent/RelatedIssueCard}/IssueIcon.svg (100%) rename frontend/src/pages/RequestDetailsPage/{ => RequestIssues/RelatedIssuesContent/RelatedIssueCard}/PullRequestIcon.svg (100%) create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIcon.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIssueCard.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/index.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssuesContent.tsx create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/index.ts create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/utils.ts create mode 100644 frontend/src/pages/RequestDetailsPage/RequestIssues/index.ts create mode 100644 frontend/src/vite-env-override.d.ts delete mode 100644 frontend/src/vite-env.d.ts diff --git a/frontend/package.json b/frontend/package.json index c0165615a..ba1f725a3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,7 +26,7 @@ "@types/highlight-words-core": "^1.2.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "date-fns": "^3.6.0", + "date-fns": "^2.30.0", "highlight-words-core": "^1.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index 1a1d7feab..e087c8027 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -1 +1,2 @@ -export { useTimeAgo} from "./useTimeAgo.js" +export { useTimeAgo } from "./useTimeAgo.js"; +export { useRequestDetails } from "./useRequestDetails.js"; diff --git a/frontend/src/hooks/useRequestDetails.ts b/frontend/src/hooks/useRequestDetails.ts new file mode 100644 index 000000000..add257965 --- /dev/null +++ b/frontend/src/hooks/useRequestDetails.ts @@ -0,0 +1,12 @@ +import { useMizuTraces } from "@/queries"; +import { isError } from "react-query"; + +export function useRequestDetails(traceId?: string) { + const query = useMizuTraces(); + const trace = traceId ? query.data?.find((t) => t.id === traceId) : undefined; + return { + isLoading: query.isLoading, + isError: isError(query), + trace, + }; +} diff --git a/frontend/src/pages/RequestDetailsPage/DataTable.tsx b/frontend/src/pages/RequestDetailsPage/DataTable.tsx deleted file mode 100644 index 0d7dc0a2d..000000000 --- a/frontend/src/pages/RequestDetailsPage/DataTable.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useMemo } from "react"; - -import { - type ColumnDef, - flexRender, - getCoreRowModel, - useReactTable, -} from "@tanstack/react-table"; - -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import type { MizuTrace } from "@/queries"; -import { useNavigate } from "react-router-dom"; - -type LevelFilter = "all" | "error" | "warning" | "info" | "debug"; - -interface DataTableProps { - columns: ColumnDef[]; - data: TData[]; -} - -export function DataTable({ - columns, - data, - filter, -}: DataTableProps & { filter: LevelFilter }) { - const navigate = useNavigate(); - - // HACK - Filter data here depending on table - // TODO - Move filtering elsewhere, use react-table like a cool kid - const filteredTraces = useMemo(() => { - if (filter === "all") { - return data; - } - return data.filter((trace) => - trace.logs.some((log) => log.level === filter), - ); - }, [data, filter]); - - // Create a table instance - const table = useReactTable({ - data: filteredTraces, - columns, - getCoreRowModel: getCoreRowModel(), - // HACK - Need to set the row id in a type-safe way - ...(filteredTraces.every( - (row) => "id" in row && typeof row.id === "string", - ) && { - getRowId: (row: TData) => row.id, - }), - }); - - 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) => ( - navigate(`/requests/${row.id}`)} // Adjust the path as needed - className="cursor-pointer" - > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - - No Results - - - )} - -
-
- ); -} diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx deleted file mode 100644 index 4548902c8..000000000 --- a/frontend/src/pages/RequestDetailsPage/RelatedIssueCard.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import { GithubIssue } from "@/queries/types"; -import IssueIcon from "./IssueIcon.svg?react"; -import { TimeAgo } from "./TimeAgo"; -import { GitHubLogoIcon } from "@radix-ui/react-icons"; -// import Highlighter from "react-highlight-words"; -import { Chunk, findAll } from "highlight-words-core"; -import { removeStopwords, eng } from "stopword" - -// import PullRequestIcon from "@radix-ui/react-icons/dist/PullRequestIcon"; -import PullRequestIcon from "./PullRequestIcon.svg?react"; - -// const insignificantWords = new Set([ -// "a", "an", "and", "are", "as", "has", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with", "set" -// ]); - - -function replaceNonWordsWithSpace(text: string) { - // Regular expression to match all non-word and non-number characters using Unicode property escapes - const regex = /[^\p{L}\p{N}]/gu; - - // Replace matched characters with a space - let result = text.replace(regex, ' '); - - // Collapse multiple spaces into a single space - result = result.replace(/\s+/g, ' '); - - return result.trim(); // Trim leading/trailing spaces -} - - -function getSignificantWords(query: string) { - const words = replaceNonWordsWithSpace(query).split(/\s+/); - return removeStopwords(words, eng); -} -const searchWords = getSignificantWords("No database connection string was provided to `neon()`. Perhaps an environment variable has not been set?"); - -const sanitize = (text: string) => text; - -const findChunks = ({ - autoEscape, - caseSensitive, - // sanitize = sanitize, - searchWords, - textToHighlight -}: { - autoEscape?: boolean, - caseSensitive?: boolean, - // sanitize?: typeof defaultSanitize, - searchWords: Array, - textToHighlight: string, -}): Array => { - textToHighlight = sanitize(textToHighlight) - console.log('text', textToHighlight) - return searchWords - .filter(searchWord => searchWord) // Remove empty words - .reduce((chunks, searchWord) => { - searchWord = sanitize(searchWord) - - if (autoEscape) { - searchWord = escapeRegExpFn(searchWord) - } - - const regex = new RegExp(`\\b${searchWord}\\b`, caseSensitive ? 'g' : 'gi') - console.log('regex', regex); - let match - while ((match = regex.exec(textToHighlight))) { - let start = match.index - let end = regex.lastIndex - // We do not return zero-length matches - if (end > start) { - chunks.push({ highlight: false, start, end }) - } - - // Prevent browsers like Firefox from getting stuck in an infinite loop - // See http://www.regexguru.com/2008/04/watch-out-for-zero-length-matches/ - if (match.index === regex.lastIndex) { - regex.lastIndex++ - } - } - - return chunks - }, [] as Array) -} - -function escapeRegExpFn(string: string): string { - return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') -} - - -export function RelatedIssueCard(issue: GithubIssue) { - console.log('------------------------------------') - console.log('words', searchWords); - console.log('title', issue.title); - console.log('body', issue.body); - console.log('------------------------------------') - - const chunks = findAll({ - searchWords: [...searchWords], - textToHighlight: issue.body, - findChunks, - }); - - // console.log('chunks', chunks) - return ( -
-
-
- - - {issue.title} -
-
- - {issue.owner}/{issue.repo} -
-
-
- {chunks.map((chunk, index) => { - if (chunk.highlight === false) { - return null; - } - const text = getSurroundingWords(issue.body, chunk.start, chunk.end); - - return {text}{index < chunks.length - 1 ? "..." : ""} ; - })} -
-
-
-
- Created: -
- {issue.updatedAt && ( - issue.updatedAt !== issue.createdAt || issue.updatedAt !== issue.closedAt - ) &&
- Updated: -
} - {issue.closedAt &&
- Closed: -
} -
-
-
- ); - -} - - - -function RelatedIcon(props: { isPr: boolean; isClosed: boolean }) { - const { isPr, isClosed } = props; - - const className = isClosed ? "text-purple-500" : "text-green-400"; - - if (isPr) { - return ; - } - - return -} - - -function getSurroundingWords(text: string, startIndex: number, endIndex: number) { - // Ensure the indices are within the bounds of the text - if (startIndex < 0 || endIndex > text.length || startIndex > endIndex) { - throw new Error('Invalid start or end index'); - } - - // Get the text before, the search term, and the text after - const before = text.substring(0, startIndex); - const match = text.substring(startIndex, endIndex); - const after = text.substring(endIndex); - - // Find the last space before the search term in the before text - const beforeLastSpaceIndex = before.lastIndexOf(' '); - - // Find the first space after the search term in the after text - const afterFirstSpaceIndex = after.indexOf(' '); - - // Extract the word before the search term - const wordBefore = beforeLastSpaceIndex !== -1 - ? before.substring(beforeLastSpaceIndex + 1) - : before; - - // Extract the word after the search term - const wordAfter = afterFirstSpaceIndex !== -1 - ? after.substring(0, afterFirstSpaceIndex) - : after; - - // Combine the word before, the match, and the word after - return <> - {wordBefore} - {match} - {wordAfter} - ; -} diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx b/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx deleted file mode 100644 index eda43a83b..000000000 --- a/frontend/src/pages/RequestDetailsPage/RelatedIssueContent.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { useRelevantIssues } from "@/queries/queries"; -import { RelatedIssueCard } from "./RelatedIssueCard"; - -export function RelatedIssueContent(props: { traceId: string }) { - const { traceId } = props; - const { data, isLoading } = useRelevantIssues(traceId); - - if (isLoading) { - return
Loading...
; - } - if (!data) { - return
No data
- } - - return ( - - - Related Issues - - See issues related to this request - - - -
    - {data.map((issue) => ( -
  • - - - -
  • - ))} -
-
-
- ); -} diff --git a/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx b/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx index be9e2e366..103dd0af7 100644 --- a/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestDetailsPage.tsx @@ -10,23 +10,11 @@ import { } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { useMizuTraces } from "@/queries"; -import { isError } from "react-query"; import { useParams } from "react-router-dom"; -import { RelatedIssueCounter } from "./RelatedIssueCounter"; +import { useRequestDetails } from "@/hooks"; import { TraceDetails } from "./RequestDetails"; -import { RelatedIssueContent } from "./RelatedIssueContent"; - -function useRequestDetails(traceId?: string) { - const query = useMizuTraces(); - const trace = traceId ? query.data?.find((t) => t.id === traceId) : undefined; - return { - isLoading: query.isLoading, - isError: isError(query), - trace, - }; -} +import { RelatedIssueCounter, RelatedIssuesContent } from "./RequestIssues"; export function RequestDetailsPage() { const { traceId } = useParams<{ traceId: string }>(); @@ -38,12 +26,13 @@ export function RequestDetailsPage() { All Errors - Related issues - {traceId && -
+ + Issues + {traceId && ( +
- } + )}
@@ -98,8 +87,8 @@ export function RequestDetailsPage() { - {traceId && } + {traceId && } - + ); } diff --git a/frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssueCounter.tsx similarity index 68% rename from frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx rename to frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssueCounter.tsx index 6442a6fcf..83a673a06 100644 --- a/frontend/src/pages/RequestDetailsPage/RelatedIssueCounter.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssueCounter.tsx @@ -11,7 +11,5 @@ export function RelatedIssueCounter({ traceId }: { traceId: string }) { return null; } - // console.log("issues", issues); - return issues && issues.length || null - // return
issues.length: {issues?.length}
; + return (issues && issues.length) || null; } diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/DependenciesList.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/DependenciesList.tsx new file mode 100644 index 000000000..f3149a0d2 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/DependenciesList.tsx @@ -0,0 +1,36 @@ +import { useDependencies } from "@/queries/queries"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; + +export function DependenciesList() { + const { + data: dependencies, + isLoading: isDependenciesLoading, + isError: isDependenciesError, + } = useDependencies(); + + if (isDependenciesLoading) { + return
Loading...
; + } + + if (isDependenciesError) { + return
Error loading dependencies
; + } + + return ( + + ); +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/IssueSection.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/IssueSection.tsx new file mode 100644 index 000000000..57978ff34 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/IssueSection.tsx @@ -0,0 +1,23 @@ +import { + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { ReactNode } from "react"; + +export function IssueSection(props: { + title: ReactNode; + description: ReactNode; + children: ReactNode; +}) { + return ( + <> + + {props.title} + {props.description} + + {props.children} + + ); +} diff --git a/frontend/src/pages/RequestDetailsPage/IssueIcon.svg b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/IssueIcon.svg similarity index 100% rename from frontend/src/pages/RequestDetailsPage/IssueIcon.svg rename to frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/IssueIcon.svg diff --git a/frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/PullRequestIcon.svg similarity index 100% rename from frontend/src/pages/RequestDetailsPage/PullRequestIcon.svg rename to frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/PullRequestIcon.svg diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIcon.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIcon.tsx new file mode 100644 index 000000000..2b84abb2d --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIcon.tsx @@ -0,0 +1,15 @@ +import IssueIcon from "./IssueIcon.svg"; +import PullRequestIcon from "./PullRequestIcon.svg"; + +export function RelatedIcon(props: { isPr: boolean; isClosed: boolean }) { + const { isPr, isClosed } = props; + + // TODO: handle other colors (like red for closed &unmerged prs) + const className = isClosed ? "text-purple-500" : "text-green-400"; + + if (isPr) { + return ; + } + + return ; +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIssueCard.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIssueCard.tsx new file mode 100644 index 000000000..7304a838e --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/RelatedIssueCard.tsx @@ -0,0 +1,89 @@ +import { GithubIssue } from "@/queries/types"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; +import { memo, useMemo } from "react"; +import { TimeAgo } from "../../../TimeAgo"; +import { RelatedIcon } from "./RelatedIcon"; +import { getChunks } from "./utils"; + +type Props = Pick< + GithubIssue, + | "body" + | "title" + | "createdAt" + | "updatedAt" + | "closedAt" + | "owner" + | "repo" + | "url" +> & { searchWords: Array }; + +export const RelatedIssueCard = memo(function RelatedIssueCard({ + searchWords, + ...issue +}: Props) { + const chunks = useMemo(() => { + return getChunks(issue.body, searchWords); + }, [issue.body, searchWords]); + + return ( + +
+
+
+ + + {issue.title} +
+
+ + + {issue.owner} + / + {issue.repo} + +
+
+
+ {chunks.map((chunk, index) => { + const section = issue.body.substring(chunk.start, chunk.end); + + if (chunk.highlight === false) { + return {section}; + } + + return ( + + {section} + + ); + })} +
+
+
+
+ Created: +
+ {issue.updatedAt && + (issue.updatedAt !== issue.createdAt || + issue.updatedAt !== issue.closedAt) && ( +
+ Updated: +
+ )} + {issue.closedAt && ( +
+ Closed: +
+ )} +
+
+
+
+ ); +}); diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/index.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/index.tsx new file mode 100644 index 000000000..d6bf38995 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/index.tsx @@ -0,0 +1 @@ +export { RelatedIssueCard } from "./RelatedIssueCard"; diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx new file mode 100644 index 000000000..9d0c3441b --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx @@ -0,0 +1,160 @@ +import { Chunk, findAll } from "highlight-words-core"; + +export function getChunks(text: string, searchWords: string[]) { + const rawChunks = findAll({ + searchWords, + textToHighlight: text, + findChunks, + }).filter((chunk) => chunk.highlight); + + const paddedChunks = addSurroundingWords(rawChunks, text); + // const paddedChunks = rawChunks.length ? addSurroundingWords(rawChunks, issue.body) : rawChunks; + console.log(searchWords); + if (paddedChunks.length > 0) { + console.log("before", rawChunks, "after", paddedChunks); + } + // return paddedChunks; + return mergeChunks(paddedChunks); +} + +function mergeChunks(chunks: Array) { + if (chunks.length === 0) { + return []; + } + + const mergedChunks: Chunk[] = [{ ...chunks[0] }]; + + // Iterate through the chunks and merge adjacent ones + for (let i = 1; i < chunks.length; i++) { + const previous = mergedChunks[mergedChunks.length - 1]; + const current = { ...chunks[i] }; + + if (previous.end > current.start) { + previous.end = Math.max(previous.end, current.end); + // Make highlight "contagious" this typically is what we want (i.e. it makes + // highlighted chunks that are adjacent to each other merge together) + // but actually isn't correct, it should only do that if there are no other "words" between them + // However we don't store that information right now. + previous.highlight = previous.highlight || current.highlight; + } else { + // Add the current chunk as a new merged chunk + mergedChunks.push(current); + } + } + + return mergedChunks; +} + +function addSurroundingWords(chunks: Array, text: string) { + const allChunks: Array = []; + + for (const chunk of chunks) { + const indexBefore = getPrefixIndex(text, chunk.start); + if (indexBefore !== null) { + allChunks.push({ + start: indexBefore, + end: chunk.start, + highlight: false, + }); + } + + allChunks.push(chunk); + + const indexAfter = getSuffixIndex(text, chunk.end); + if (indexAfter !== null) { + allChunks.push({ start: chunk.end, end: indexAfter, highlight: false }); + } + } + + return allChunks; +} + +function getPrefixIndex(text: string, index: number) { + if (index <= 0) { + return null; // No word before the start of the string + } + + // Get the substring up to the specified index + const substring = text.slice(0, index); + + // Regular expression to find the last word in the substring + const regex = /([^\w\s]?\b\w+\b[^\w]*$)/; + + // Execute the regex on the substring + const match = regex.exec(substring); + + return match ? index - match[0].length : null; +} + +function getSuffixIndex(text: string, index: number) { + if (index >= text.length) { + return null; // No word after the end of the string + } + + // Get the substring starting from the adjusted index + const substring = text.slice(index); + + // Regular expression to find the first word in the substring + const regex = /^[^\w]*\b\w+\b[^\w\s]*/; + + // Execute the regex on the substring + const match = regex.exec(substring); + console.log("match", match); + return match ? index + match[0].length : null; +} + +const sanitize = (text: string) => text; + +const findChunks = ({ + autoEscape, + caseSensitive, + // sanitize = sanitize, + searchWords, + textToHighlight, +}: { + autoEscape?: boolean; + caseSensitive?: boolean; + // sanitize?: typeof defaultSanitize, + searchWords: Array; + textToHighlight: string; +}): Array => { + textToHighlight = sanitize(textToHighlight); + return searchWords + .filter((searchWord) => searchWord) // Remove empty words + .reduce( + (chunks, searchWord) => { + searchWord = sanitize(searchWord); + + if (autoEscape) { + searchWord = escapeRegExpFn(searchWord); + } + + const regex = new RegExp( + `\\b${searchWord}\\b`, + caseSensitive ? "g" : "gi", + ); + let match; + while ((match = regex.exec(textToHighlight))) { + const start = match.index; + const end = regex.lastIndex; + // We do not return zero-length matches + if (end > start) { + chunks.push({ highlight: false, start, end }); + } + + // Prevent browsers like Firefox from getting stuck in an infinite loop + // See http://www.regexuru.com/2008/04/watch-out-for-zero-length-matches/ + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + } + + return chunks; + }, + [] as Array, + ); +}; + +function escapeRegExpFn(string: string): string { + return string.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx new file mode 100644 index 000000000..b5df8a9a6 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx @@ -0,0 +1,76 @@ +import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; + +import { useRequestDetails } from "@/hooks"; +import { MizuLog } from "@/queries"; +import { useRelevantIssues } from "@/queries/queries"; +import { useMemo } from "react"; +import { RelatedIssueCard } from "./RelatedIssueCard"; +import { getSignificantWords } from "./utils"; + +export function RelatedIssueList(props: { traceId: string }) { + const { traceId } = props; + const { + data: issues, + isLoading: issuesLoading, + isError: issuesError, + } = useRelevantIssues(traceId); + const { + trace: details, + isError: isDetailsError, + isLoading: isDetailsLoading, + } = useRequestDetails(traceId); + + const relatedError = details?.logs.find((log) => log.callerLocation); + const query = relatedError && getQueryFromLog(relatedError); + + const searchWords = useMemo(() => { + if (!query) { + return []; + } + + return getSignificantWords(query); + }, [query]); + + if (issuesLoading && isDetailsLoading) { + return
Loading...
; + } + + if (isDetailsError && issuesError) { + return
Error loading issues
; + } + + if (!issues || issues.length === 0 || !query) { + return
No data
; + } + + return ( +
    + {issues.length === 0 && ( +
  • +
    + + No related issues found +
    +
  • + )} + {issues.map((issue) => ( +
  • + + + +
  • + ))} +
+ ); +} + +function getQueryFromLog(log: MizuLog) { + const message = + typeof log.message === "string" + ? log.message + : "message" in log.message && + typeof log.message.message === "string" && + log.message.message; + + return typeof message === "string" ? message : undefined; +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssuesContent.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssuesContent.tsx new file mode 100644 index 000000000..e6b6579a5 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssuesContent.tsx @@ -0,0 +1,29 @@ +import { Card } from "@/components/ui/card"; +import { DependenciesList } from "./DependenciesList"; +import { IssueSection } from "./IssueSection"; +import { RelatedIssueList } from "./RelatedIssueList"; + +export function RelatedIssuesContent(props: { traceId: string }) { + const { traceId } = props; + + return ( +
+ + + + + + + + + + +
+ ); +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/index.ts b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/index.ts new file mode 100644 index 000000000..75d5fdde2 --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/index.ts @@ -0,0 +1 @@ +export { RelatedIssuesContent } from "./RelatedIssuesContent"; diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/utils.ts b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/utils.ts new file mode 100644 index 000000000..90bf3ef8f --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/utils.ts @@ -0,0 +1,19 @@ +import { eng, removeStopwords } from "stopword"; + +export function getSignificantWords(query: string) { + const words = replaceNonWordsWithSpace(query).split(/\s+/); + return removeStopwords(words, eng); +} + +function replaceNonWordsWithSpace(text: string) { + // Regular expression to match all non-word and non-number characters using Unicode property escapes + const regex = /[^\p{L}\p{N}]/gu; + + // Replace matched characters with a space + let result = text.replace(regex, " "); + + // Collapse multiple spaces into a single space + result = result.replace(/\s+/g, " "); + + return result.trim(); // Trim leading/trailing spaces +} diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/index.ts b/frontend/src/pages/RequestDetailsPage/RequestIssues/index.ts new file mode 100644 index 000000000..da3d90efe --- /dev/null +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/index.ts @@ -0,0 +1,2 @@ +export { RelatedIssuesContent } from "./RelatedIssuesContent"; +export { RelatedIssueCounter } from "./RelatedIssueCounter"; diff --git a/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx b/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx index 14f758e8c..02bb2ee08 100644 --- a/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx +++ b/frontend/src/pages/RequestDetailsPage/TimeAgo.tsx @@ -1,4 +1,3 @@ - import { useTimeAgo } from "../../hooks"; export function TimeAgo(props: { date: string }) { @@ -8,5 +7,5 @@ export function TimeAgo(props: { date: string }) { return null; } - return {time} + return {time}; } diff --git a/frontend/src/queries/queries.ts b/frontend/src/queries/queries.ts index 452a887f7..becacdd8c 100644 --- a/frontend/src/queries/queries.ts +++ b/frontend/src/queries/queries.ts @@ -7,6 +7,7 @@ import { import { objectWithKeyAndValue } from "@/utils"; import { + DependenciesSchema, GitHubIssuesSchema, MizuApiLogResponseSchema, type MizuLog, @@ -161,3 +162,23 @@ async function fetchRelevantIssues( console.error("Error fetching GitHub issue for a trace: ", e); } } + +export function useDependencies() { + return useQuery({ + queryKey: ["dependencies"], + queryFn: fetchDependencies, + }); +} + +async function fetchDependencies() { + try { + const response = await fetch("/v0/dependencies", { + mode: "cors", + }); + + const data = await response.json(); + return DependenciesSchema.parse(data); + } catch (e: unknown) { + console.error("Error fetching dependencies: ", e); + } +} diff --git a/frontend/src/queries/types.ts b/frontend/src/queries/types.ts index 089c3b2b8..b4af2e37a 100644 --- a/frontend/src/queries/types.ts +++ b/frontend/src/queries/types.ts @@ -282,4 +282,21 @@ export const GitHubIssueSchema = z.object({ closedAt: z.string().nullable(), }); +export type GithubIssue = z.infer; + export const GitHubIssuesSchema = z.array(GitHubIssueSchema); + +export const DependencySchema = z.object({ + name: z.string(), + version: z.string(), + repository: z.object({ + owner: z.string(), + repo: z.string(), + url: z.string(), + }), +}); +export type Dependency = z.infer; + +export const DependenciesSchema = z.array(DependencySchema); + +export type Dependencies = z.infer; diff --git a/frontend/src/vite-env-override.d.ts b/frontend/src/vite-env-override.d.ts new file mode 100644 index 000000000..dc8730432 --- /dev/null +++ b/frontend/src/vite-env-override.d.ts @@ -0,0 +1,8 @@ +declare module "*.svg" { + import * as React from "react"; + + const ReactComponent: React.FunctionComponent< + React.ComponentProps<"svg"> & { title?: string } + >; + export default ReactComponent; +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a..000000000 --- a/frontend/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 97ede7ee6..b436652c0 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -5,7 +5,8 @@ "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, - "strict": true + "strict": true, + "types": ["vite-plugin-svgr/client-default"] }, "include": ["vite.config.ts"] } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index d86e6af54..13c92daf7 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,10 +1,16 @@ import path from "node:path"; import react from "@vitejs/plugin-react-swc"; -import svgr from "vite-plugin-svgr"; import { defineConfig } from "vite"; +import svgr from "vite-plugin-svgr"; export default defineConfig({ - plugins: [react(), svgr()], + plugins: [ + react(), + svgr({ + svgrOptions: { exportType: "default", ref: true }, + include: "**/*.svg", + }), + ], resolve: { alias: { "@": path.resolve(__dirname, "./src"), diff --git a/package-lock.json b/package-lock.json index 31c0bacb3..3c60abec5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ "@types/highlight-words-core": "^1.2.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "date-fns": "^3.6.0", + "date-fns": "^2.30.0", "highlight-words-core": "^1.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -120,6 +120,21 @@ "wrangler": "^3.57.0" } }, + "frontend/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "mizu": { "version": "1.0.0", "extraneous": true, @@ -5537,15 +5552,6 @@ "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", "dev": true }, - "node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", From 7d76e5c0856e95a87656fc59312d566f56f2cdd6 Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Fri, 14 Jun 2024 11:37:17 +0200 Subject: [PATCH 6/6] Feedback on pr --- .../RelatedIssueCard/utils.tsx | 7 ------- .../RelatedIssuesContent/RelatedIssueList.tsx | 15 ++++++--------- .../RelatedIssuesContent/RelatedIssuesContent.tsx | 2 +- frontend/src/queries/queries.ts | 4 +++- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx index 9d0c3441b..0b0b5d680 100644 --- a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueCard/utils.tsx @@ -8,12 +8,6 @@ export function getChunks(text: string, searchWords: string[]) { }).filter((chunk) => chunk.highlight); const paddedChunks = addSurroundingWords(rawChunks, text); - // const paddedChunks = rawChunks.length ? addSurroundingWords(rawChunks, issue.body) : rawChunks; - console.log(searchWords); - if (paddedChunks.length > 0) { - console.log("before", rawChunks, "after", paddedChunks); - } - // return paddedChunks; return mergeChunks(paddedChunks); } @@ -99,7 +93,6 @@ function getSuffixIndex(text: string, index: number) { // Execute the regex on the substring const match = regex.exec(substring); - console.log("match", match); return match ? index + match[0].length : null; } diff --git a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx index b5df8a9a6..819eac0c4 100644 --- a/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx +++ b/frontend/src/pages/RequestDetailsPage/RequestIssues/RelatedIssuesContent/RelatedIssueList.tsx @@ -40,19 +40,16 @@ export function RelatedIssueList(props: { traceId: string }) { } if (!issues || issues.length === 0 || !query) { - return
No data
; + return ( +
+ + No related issues found +
+ ); } return (