diff --git a/.env.example b/.env.example index f38004ef48..2e1725cf0b 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,7 @@ DIRECT_URL=${DATABASE_URL} REMIX_APP_PORT=3030 APP_ENV=development APP_ORIGIN=http://localhost:3030 +ELECTRIC_ORIGIN=http://localhost:3060 NODE_ENV=development V3_ENABLED=true diff --git a/apps/webapp/app/components/code/CodeBlock.tsx b/apps/webapp/app/components/code/CodeBlock.tsx index 1d0925a53d..62a4760244 100644 --- a/apps/webapp/app/components/code/CodeBlock.tsx +++ b/apps/webapp/app/components/code/CodeBlock.tsx @@ -1,7 +1,7 @@ import { Clipboard, ClipboardCheck } from "lucide-react"; import type { Language, PrismTheme } from "prism-react-renderer"; import { Highlight, Prism } from "prism-react-renderer"; -import { forwardRef, useCallback, useState } from "react"; +import { forwardRef, ReactNode, useCallback, useState } from "react"; import { cn } from "~/utils/cn"; import { Paragraph } from "../primitives/Paragraph"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip"; @@ -385,7 +385,7 @@ function Chrome({ title }: { title?: string }) { ); } -export function TitleRow({ title }: { title: string }) { +export function TitleRow({ title }: { title: ReactNode }) { return (
diff --git a/apps/webapp/app/components/code/InlineCode.tsx b/apps/webapp/app/components/code/InlineCode.tsx index 8ff5b99163..120e864250 100644 --- a/apps/webapp/app/components/code/InlineCode.tsx +++ b/apps/webapp/app/components/code/InlineCode.tsx @@ -1,7 +1,7 @@ import { cn } from "~/utils/cn"; const inlineCode = - "px-1 py-0.5 rounded border border-charcoal-700 bg-charcoal-800 text-charcoal-200 font-mono"; + "px-1 py-0.5 rounded border border-charcoal-700 bg-charcoal-800 text-charcoal-200 font-mono text-wrap"; const variants = { "extra-extra-small": "text-xxs", diff --git a/apps/webapp/app/components/primitives/Resizable.tsx b/apps/webapp/app/components/primitives/Resizable.tsx index 776b210f30..ac4cba25cd 100644 --- a/apps/webapp/app/components/primitives/Resizable.tsx +++ b/apps/webapp/app/components/primitives/Resizable.tsx @@ -1,32 +1,35 @@ "use client"; -import * as ResizablePrimitive from "react-resizable-panels"; +import React from "react"; +import { PanelGroup, Panel, PanelResizer } from "react-window-splitter"; import { cn } from "~/utils/cn"; -const ResizablePanelGroup = ({ - className, - ...props -}: React.ComponentProps) => ( - ) => ( + ); -const ResizablePanel = ResizablePrimitive.Panel; +const ResizablePanel = Panel; const ResizableHandle = ({ - withHandle, + withHandle = true, className, ...props -}: React.ComponentProps & { +}: React.ComponentProps & { withHandle?: boolean; }) => ( - div]:rotate-90", + "focus-visible:ring-ring group relative flex w-0.75 items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary/50 focus-visible:ring-offset-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", className )} + size="3px" {...props} >
@@ -37,7 +40,9 @@ const ResizableHandle = ({ ))}
)} -
+ ); export { ResizableHandle, ResizablePanel, ResizablePanelGroup }; + +export type ResizableSnapshot = React.ComponentProps["snapshot"]; diff --git a/apps/webapp/app/components/primitives/TreeView/TreeView.tsx b/apps/webapp/app/components/primitives/TreeView/TreeView.tsx index dd19b42a13..e6d48b8cc4 100644 --- a/apps/webapp/app/components/primitives/TreeView/TreeView.tsx +++ b/apps/webapp/app/components/primitives/TreeView/TreeView.tsx @@ -241,7 +241,7 @@ export function useTree({ index, }); }, - overscan: 20, + overscan: 50, }); const scrollToNodeFn = useCallback( diff --git a/apps/webapp/app/components/runs/v3/InspectorTimeline.tsx b/apps/webapp/app/components/runs/v3/InspectorTimeline.tsx new file mode 100644 index 0000000000..1d574ff6a0 --- /dev/null +++ b/apps/webapp/app/components/runs/v3/InspectorTimeline.tsx @@ -0,0 +1,63 @@ +import { ReactNode } from "react"; +import { cn } from "~/utils/cn"; + +type RunTimelineItemProps = { + title: ReactNode; + subtitle?: ReactNode; + state: "complete" | "error"; +}; + +export function RunTimelineEvent({ title, subtitle, state }: RunTimelineItemProps) { + return ( +
+
+
+
+
+ {title} + {subtitle ? {subtitle} : null} +
+
+ ); +} + +type RunTimelineLineProps = { + title: ReactNode; + state: "complete" | "delayed" | "inprogress"; +}; + +export function RunTimelineLine({ title, state }: RunTimelineLineProps) { + return ( +
+
+
+
+
+ {title} +
+
+ ); +} diff --git a/apps/webapp/app/components/runs/v3/RunInspector.tsx b/apps/webapp/app/components/runs/v3/RunInspector.tsx new file mode 100644 index 0000000000..62508edafe --- /dev/null +++ b/apps/webapp/app/components/runs/v3/RunInspector.tsx @@ -0,0 +1,650 @@ +import { CheckIcon, ClockIcon, CloudArrowDownIcon, QueueListIcon } from "@heroicons/react/20/solid"; +import { Link } from "@remix-run/react"; +import { formatDuration, formatDurationMilliseconds, TaskRunError } from "@trigger.dev/core/v3"; +import { useEffect } from "react"; +import { useTypedFetcher } from "remix-typedjson"; +import { ExitIcon } from "~/assets/icons/ExitIcon"; +import { CodeBlock, TitleRow } from "~/components/code/CodeBlock"; +import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel"; +import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { Callout } from "~/components/primitives/Callout"; +import { DateTime, DateTimeAccurate } from "~/components/primitives/DateTime"; +import { Header2, Header3 } from "~/components/primitives/Headers"; +import { Paragraph } from "~/components/primitives/Paragraph"; +import * as Property from "~/components/primitives/PropertyTable"; +import { Spinner } from "~/components/primitives/Spinner"; +import { TabButton, TabContainer } from "~/components/primitives/Tabs"; +import { TextLink } from "~/components/primitives/TextLink"; +import { InfoIconTooltip, SimpleTooltip } from "~/components/primitives/Tooltip"; +import { LiveTimer } from "~/components/runs/v3/LiveTimer"; +import { RunIcon } from "~/components/runs/v3/RunIcon"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { useProject } from "~/hooks/useProject"; +import { useSearchParams } from "~/hooks/useSearchParam"; +import { RawRun } from "~/hooks/useSyncTraceRuns"; +import { loader } from "~/routes/resources.runs.$runParam"; +import { cn } from "~/utils/cn"; +import { formatCurrencyAccurate } from "~/utils/numberFormatter"; +import { + v3RunDownloadLogsPath, + v3RunPath, + v3RunSpanPath, + v3RunsPath, + v3SchedulePath, + v3TraceSpanPath, +} from "~/utils/pathBuilder"; +import { TraceSpan } from "~/utils/taskEvent"; +import { SpanLink } from "~/v3/eventRepository.server"; +import { isFinalRunStatus } from "~/v3/taskStatus"; +import { RunTimelineEvent, RunTimelineLine } from "./InspectorTimeline"; +import { RunTag } from "./RunTag"; +import { TaskRunStatusCombo } from "./TaskRunStatus"; + +/** + * The RunInspector displays live information about a run. + * Most of that data comes in as params but for some we need to fetch it. + */ +export function RunInspector({ + run, + span, + runParam, + closePanel, +}: { + run?: RawRun; + span?: TraceSpan; + runParam: string; + closePanel?: () => void; +}) { + const organization = useOrganization(); + const project = useProject(); + const { value, replace } = useSearchParams(); + const tab = value("tab"); + + const fetcher = useTypedFetcher(); + + useEffect(() => { + if (run?.friendlyId === undefined) return; + fetcher.load(`/resources/runs/${run.friendlyId}`); + }, [run?.friendlyId, run?.updatedAt]); + + if (!run) { + return ( +
+
+
+ + + + +
+ {closePanel && ( +
+
+
+ ); + } + + const environment = project.environments.find((e) => e.id === run.runtimeEnvironmentId); + const clientRunData = fetcher.state === "idle" ? fetcher.data : undefined; + + return ( +
+
+
+ + + {run.taskIdentifier} + +
+ {closePanel && ( +
+
+ + { + replace({ tab: "overview" }); + }} + shortcut={{ key: "o" }} + > + Overview + + { + replace({ tab: "detail" }); + }} + shortcut={{ key: "d" }} + > + Detail + + { + replace({ tab: "context" }); + }} + shortcut={{ key: "c" }} + > + Context + + +
+
+
+ {tab === "detail" ? ( +
+ + + Status + + {run ? : } + + + + Task + + + {run.taskIdentifier} + + } + content={`Filter runs by ${run.taskIdentifier}`} + /> + + + + Version + + {clientRunData ? ( + clientRunData?.version ? ( + clientRunData.version + ) : ( + + Never started + + + ) + ) : ( + + )} + + + + SDK version + + {clientRunData ? ( + clientRunData?.sdkVersion ? ( + clientRunData.sdkVersion + ) : ( + + Never started + + + ) + ) : ( + + )} + + + + Test run + + {run.isTest ? : "–"} + + + {environment && ( + + Environment + + + + + )} + + + Schedule + + {clientRunData ? ( + clientRunData.schedule ? ( +
+
+ + {clientRunData.schedule.generatorExpression} + + ({clientRunData.schedule.timezone}) +
+ + {clientRunData.schedule.description} + + } + content={`Go to schedule ${clientRunData.schedule.friendlyId}`} + /> +
+ ) : ( + "No schedule" + ) + ) : ( + + )} +
+
+ + Queue + + {clientRunData ? ( + <> +
Name: {clientRunData.queue.name}
+
+ Concurrency key:{" "} + {clientRunData.queue.concurrencyKey + ? clientRunData.queue.concurrencyKey + : "–"} +
+ + ) : ( + + )} +
+
+ + Time to live (TTL) + {run.ttl ?? "–"} + + + Tags + + {clientRunData ? ( + clientRunData.tags.length === 0 ? ( + "–" + ) : ( +
+ {clientRunData.tags.map((tag) => ( + + + + } + content={`Filter runs by ${tag}`} + /> + ))} +
+ ) + ) : ( + + )} +
+
+ {span?.links && span.links.length > 0 && ( + + Links + +
+ {span.links.map((link, index) => ( + + ))} +
+
+
+ )} + + Run invocation cost + + {run.baseCostInCents > 0 + ? formatCurrencyAccurate(run.baseCostInCents / 100) + : "–"} + + + + Compute cost + + {run.costInCents > 0 ? formatCurrencyAccurate(run.costInCents / 100) : "–"} + + + + Total cost + + {run.costInCents > 0 + ? formatCurrencyAccurate((run.baseCostInCents + run.costInCents) / 100) + : "–"} + + + + Usage duration + + {run.usageDurationMs > 0 + ? formatDurationMilliseconds(run.usageDurationMs, { style: "short" }) + : "–"} + + +
+
+ ) : tab === "context" ? ( +
+ {clientRunData ? ( + + ) : ( +
+ + Context loading… + + + } + /> +
+ )} +
+ ) : ( +
+
+ +
+ + <> + {clientRunData ? ( + <> + {clientRunData.payload !== undefined && ( + + )} + {clientRunData.error !== undefined ? ( + + ) : clientRunData.output !== undefined ? ( + + ) : null} + + ) : ( +
+ + Payload loading… + + + } + /> +
+ )} + +
+ )} +
+
+
+
+ {run.friendlyId !== runParam && ( + + Focus on run + + )} +
+
+ {run.logsDeletedAt === null ? ( + + Download logs + + ) : null} +
+
+
+ ); +} + +function PropertyLoading() { + return ; +} + +function RunTimeline({ run }: { run: RawRun }) { + const createdAt = new Date(run.createdAt); + const startedAt = run.startedAt ? new Date(run.startedAt) : null; + const delayUntil = run.delayUntil ? new Date(run.delayUntil) : null; + const expiredAt = run.expiredAt ? new Date(run.expiredAt) : null; + const updatedAt = new Date(run.updatedAt); + + const isFinished = isFinalRunStatus(run.status); + + return ( +
+ } + state="complete" + /> + {delayUntil && !expiredAt ? ( + {formatDuration(createdAt, delayUntil)} delay + ) : ( + + + + Delayed until {run.ttl && <>(TTL {run.ttl})} + + + ) + } + state={run.startedAt ? "complete" : "delayed"} + /> + ) : startedAt ? ( + + ) : ( + + {" "} + {run.ttl && <>(TTL {run.ttl})} + + } + state={run.startedAt || run.expiredAt ? "complete" : "inprogress"} + /> + )} + {expiredAt ? ( + } + state="error" + /> + ) : startedAt ? ( + <> + } + state="complete" + /> + {isFinished ? ( + <> + + } + state="complete" + /> + + ) : ( + + + + + + + } + state={"inprogress"} + /> + )} + + ) : null} +
+ ); +} + +function RunError({ error }: { error: TaskRunError }) { + switch (error.type) { + case "STRING_ERROR": + case "CUSTOM_ERROR": { + return ( +
+ +
+ ); + } + case "BUILT_IN_ERROR": + case "INTERNAL_ERROR": { + const name = "name" in error ? error.name : error.code; + return ( +
+ {name} + {error.message && {error.message}} + {error.stackTrace && ( + + )} +
+ ); + } + } +} + +function PacketDisplay({ + data, + dataType, + title, +}: { + data: string; + dataType: string; + title: string; +}) { + switch (dataType) { + case "application/store": { + return ( +
+ + {title} + + + Download + +
+ ); + } + case "text/plain": { + return ( + + ); + } + default: { + return ( + + ); + } + } +} + +function SpanLinkElement({ link }: { link: SpanLink }) { + const organization = useOrganization(); + const project = useProject(); + + switch (link.type) { + case "run": { + return ( + + {link.title} + + ); + } + case "span": { + return ( + + {link.title} + + ); + } + } + + return null; +} diff --git a/apps/webapp/app/components/runs/v3/SpanInspector.tsx b/apps/webapp/app/components/runs/v3/SpanInspector.tsx new file mode 100644 index 0000000000..4b6ac79794 --- /dev/null +++ b/apps/webapp/app/components/runs/v3/SpanInspector.tsx @@ -0,0 +1,313 @@ +import { ExitIcon } from "~/assets/icons/ExitIcon"; +import { CodeBlock } from "~/components/code/CodeBlock"; +import { Button } from "~/components/primitives/Buttons"; +import { DateTimeAccurate } from "~/components/primitives/DateTime"; +import { Header2 } from "~/components/primitives/Headers"; +import * as Property from "~/components/primitives/PropertyTable"; +import { TabButton, TabContainer } from "~/components/primitives/Tabs"; +import { TextLink } from "~/components/primitives/TextLink"; +import { InfoIconTooltip, SimpleTooltip } from "~/components/primitives/Tooltip"; +import { RunIcon } from "~/components/runs/v3/RunIcon"; +import { SpanEvents } from "~/components/runs/v3/SpanEvents"; +import { SpanTitle } from "~/components/runs/v3/SpanTitle"; +import { TaskRunAttemptStatusCombo } from "~/components/runs/v3/TaskRunAttemptStatus"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { useProject } from "~/hooks/useProject"; +import { useSearchParams } from "~/hooks/useSearchParam"; +import { cn } from "~/utils/cn"; +import { v3RunPath, v3RunsPath, v3TraceSpanPath } from "~/utils/pathBuilder"; +import { TraceSpan } from "~/utils/taskEvent"; +import { SpanLink } from "~/v3/eventRepository.server"; +import { RunTimelineEvent, RunTimelineLine } from "./InspectorTimeline"; +import { Spinner } from "~/components/primitives/Spinner"; +import { LiveTimer } from "./LiveTimer"; +import { formatDuration, nanosecondsToMilliseconds } from "@trigger.dev/core/v3"; + +export function SpanInspector({ + span, + runParam, + closePanel, +}: { + span?: TraceSpan; + runParam?: string; + closePanel?: () => void; +}) { + const organization = useOrganization(); + const project = useProject(); + const { value, replace } = useSearchParams(); + let tab = value("tab"); + + if (tab === "context") { + tab = "overview"; + } + + if (span === undefined) { + return null; + } + + return ( +
+
+
+ + + + +
+ {runParam && closePanel && ( +
+
+ + { + replace({ tab: "overview" }); + }} + shortcut={{ key: "o" }} + > + Overview + + { + replace({ tab: "detail" }); + }} + shortcut={{ key: "d" }} + > + Detail + + +
+
+
+ {tab === "detail" ? ( +
+ + + Status + + + + + + Task + + + {span.taskSlug} + + } + content={`Filter runs by ${span.taskSlug}`} + /> + + + {span.idempotencyKey && ( + + Idempotency key + {span.idempotencyKey} + + )} + + Version + + {span.workerVersion ? ( + span.workerVersion + ) : ( + + Never started + + + )} + + + {span.links && span.links.length > 0 && ( + + Links + +
+ {span.links.map((link, index) => ( + + ))} +
+
+
+ )} +
+
+ ) : ( +
+ {span.level === "TRACE" ? ( + <> +
+ +
+ + + ) : ( +
+ } + state="complete" + /> +
+ )} + + + Message + {span.message} + + {span.links && span.links.length > 0 && ( + + Links + +
+ {span.links.map((link, index) => ( + + ))} +
+
+
+ )} +
+ + {span.events !== undefined && } + {span.properties !== undefined && ( + + )} +
+ )} +
+
+
+ ); +} + +type TimelineProps = { + startTime: Date; + duration: number; + inProgress: boolean; + isError: boolean; +}; + +export function SpanTimeline({ startTime, duration, inProgress, isError }: TimelineProps) { + const state = isError ? "error" : inProgress ? "pending" : "complete"; + return ( + <> +
+ } + state="complete" + /> + {state === "pending" ? ( + + + + + + + } + state={"inprogress"} + /> + ) : ( + <> + + + } + state={isError ? "error" : "complete"} + /> + + )} +
+ + ); +} + +function SpanLinkElement({ link }: { link: SpanLink }) { + const organization = useOrganization(); + const project = useProject(); + + switch (link.type) { + case "run": { + return ( + + {link.title} + + ); + } + case "span": { + return ( + + {link.title} + + ); + } + } + + return null; +} diff --git a/apps/webapp/app/components/runs/v3/SpanTitle.tsx b/apps/webapp/app/components/runs/v3/SpanTitle.tsx index d065131a7c..7e36652df8 100644 --- a/apps/webapp/app/components/runs/v3/SpanTitle.tsx +++ b/apps/webapp/app/components/runs/v3/SpanTitle.tsx @@ -2,7 +2,6 @@ import { ChevronRightIcon } from "@heroicons/react/20/solid"; import { TaskEventStyle } from "@trigger.dev/core/v3"; import type { TaskEventLevel } from "@trigger.dev/database"; import { Fragment } from "react"; -import { RunEvent } from "~/presenters/v3/RunPresenter.server"; import { cn } from "~/utils/cn"; type SpanTitleProps = { @@ -107,9 +106,15 @@ function eventTextClassName(event: Pick -) { +type RunEvent = { + isError: boolean; + style: TaskEventStyle; + level: TaskEventLevel; + isPartial: boolean; + isCancelled: boolean; +}; + +export function eventBackgroundClassName(event: RunEvent) { if (event.isError) { return "bg-error"; } diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index 50575a1a37..0d81c61d85 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -31,6 +31,7 @@ const EnvironmentSchema = z.object({ REMIX_APP_PORT: z.string().optional(), LOGIN_ORIGIN: z.string().default("http://localhost:3030"), APP_ORIGIN: z.string().default("http://localhost:3030"), + ELECTRIC_ORIGIN: z.string(), APP_ENV: z.string().default(process.env.NODE_ENV), SERVICE_NAME: z.string().default("trigger.dev webapp"), SECRET_STORE: SecretStoreOptionsSchema.default("DATABASE"), diff --git a/apps/webapp/app/hooks/useSyncRunPage.ts b/apps/webapp/app/hooks/useSyncRunPage.ts new file mode 100644 index 0000000000..e0268448cb --- /dev/null +++ b/apps/webapp/app/hooks/useSyncRunPage.ts @@ -0,0 +1,16 @@ +import { useSyncTrace } from "./useSyncTrace"; +import { useSyncTraceRuns } from "./useSyncTraceRuns"; + +type Params = { + origin: string; + traceId: string; +}; + +export function useSyncRunPage({ origin, traceId }: Params) { + const { runs } = useSyncTraceRuns({ origin, traceId }); + const { events } = useSyncTrace({ origin, traceId }); + + const isUpToDate = runs !== undefined && events !== undefined; + + return { isUpToDate, runs, events }; +} diff --git a/apps/webapp/app/hooks/useSyncTrace.ts b/apps/webapp/app/hooks/useSyncTrace.ts new file mode 100644 index 0000000000..494e4d9914 --- /dev/null +++ b/apps/webapp/app/hooks/useSyncTrace.ts @@ -0,0 +1,19 @@ +import { useShape } from "@electric-sql/react"; +import { TaskEvent } from "@trigger.dev/database"; +import { createTraceTreeFromEvents } from "~/utils/taskEvent"; + +type Params = { + origin: string; + traceId: string; +}; + +export type Trace = ReturnType; +export type TraceEvent = Trace["events"][number]; + +export function useSyncTrace({ origin, traceId }: Params) { + const { data, error, isError } = useShape({ + url: `${origin}/sync/traces/${traceId}`, + }); + + return { error, isError, events: data ? (data as any as TaskEvent[]) : undefined }; +} diff --git a/apps/webapp/app/hooks/useSyncTraceRuns.ts b/apps/webapp/app/hooks/useSyncTraceRuns.ts new file mode 100644 index 0000000000..6a341636b5 --- /dev/null +++ b/apps/webapp/app/hooks/useSyncTraceRuns.ts @@ -0,0 +1,18 @@ +import { Prettify } from "@trigger.dev/core"; +import { TaskRun } from "@trigger.dev/database"; +import { SyncedShapeData, useSyncedShape } from "./useSyncedShape"; + +type Params = { + origin: string; + traceId: string; +}; + +export type RawRun = Prettify>; + +export function useSyncTraceRuns({ origin, traceId }: Params) { + const { data, error, isError } = useSyncedShape({ + url: `${origin}/sync/traces/runs/${traceId}`, + }); + + return { runs: data, error, isError }; +} diff --git a/apps/webapp/app/hooks/useSyncedShape.ts b/apps/webapp/app/hooks/useSyncedShape.ts new file mode 100644 index 0000000000..419979f3a3 --- /dev/null +++ b/apps/webapp/app/hooks/useSyncedShape.ts @@ -0,0 +1,71 @@ +import { useShape } from "@electric-sql/react"; + +export type ShapeInput = Parameters[0]; +export type ShapeOutput = { + error: Error | false; + isError: boolean; + data: S[] | undefined; +}; + +export type SyncedShapeData = { + [K in keyof T]: T[K] extends Date + ? string + : T[K] extends Date | null + ? string | null + : T[K] extends BigInt + ? number + : T[K] extends BigInt | null + ? number | null + : T[K] extends object + ? SyncedShapeData + : T[K]; +}; + +export function useSyncedShape(props: ShapeInput): ShapeOutput> { + const output = useShape(props) as any; + + return { + error: output.error, + isError: output.isError, + data: output.data + ? (transformInput(output.data as InputObject) as SyncedShapeData[]) + : undefined, + }; +} + +type InputObject = { + [key: string]: Value; +}[]; + +type Value = + | string + | number + | boolean + | bigint + | null + | Value[] + | { + [key: string]: Value; + }; + +function transformInput(input: InputObject): SyncedShapeData { + return input.map((value) => transformValue(value)); +} + +function transformValue(value: Value): Value { + if (Array.isArray(value)) { + return value.map(transformValue); + } else if (typeof value === "object" && value !== null) { + const result: { [key: string]: Value } = {}; + + for (const key in value) { + result[key] = transformValue(value[key]); + } + + return result; + } else if (typeof value === "bigint") { + return Number(value); + } else { + return value; + } +} diff --git a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts index a59bc5d02d..9ce94f9d03 100644 --- a/apps/webapp/app/presenters/v3/RunListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/RunListPresenter.server.ts @@ -3,9 +3,9 @@ import parse from "parse-duration"; import { type Direction } from "~/components/runs/RunStatuses"; import { sqlDatabaseSchema } from "~/db.server"; import { displayableEnvironment } from "~/models/runtimeEnvironment.server"; -import { getAllTaskIdentifiers } from "~/models/task.server"; import { isCancellableRunStatus, isFinalRunStatus } from "~/v3/taskStatus"; import { BasePresenter } from "./basePresenter.server"; +import { getAllTaskIdentifiers } from "~/models/task.server"; export type RunListOptions = { userId?: string; diff --git a/apps/webapp/app/presenters/v3/RunPresenterElectric.server.ts b/apps/webapp/app/presenters/v3/RunPresenterElectric.server.ts new file mode 100644 index 0000000000..7e1e3ef1b8 --- /dev/null +++ b/apps/webapp/app/presenters/v3/RunPresenterElectric.server.ts @@ -0,0 +1,87 @@ +import { PrismaClient, prisma } from "~/db.server"; +import { getUsername } from "~/utils/username"; +import { isFinalRunStatus } from "~/v3/taskStatus"; + +type Result = Awaited>; +export type Run = Result["run"]; + +export class RunPresenter { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call({ + userId, + projectSlug, + organizationSlug, + runFriendlyId, + }: { + userId: string; + projectSlug: string; + organizationSlug: string; + runFriendlyId: string; + }) { + const run = await this.#prismaClient.taskRun.findFirstOrThrow({ + select: { + id: true, + number: true, + traceId: true, + spanId: true, + friendlyId: true, + status: true, + completedAt: true, + logsDeletedAt: true, + runtimeEnvironment: { + select: { + id: true, + type: true, + slug: true, + organizationId: true, + orgMember: { + select: { + user: { + select: { + id: true, + name: true, + displayName: true, + }, + }, + }, + }, + }, + }, + }, + where: { + friendlyId: runFriendlyId, + project: { + slug: projectSlug, + }, + }, + }); + + return { + run: { + id: run.id, + number: run.number, + friendlyId: run.friendlyId, + traceId: run.traceId, + spanId: run.spanId, + status: run.status, + isFinished: isFinalRunStatus(run.status), + completedAt: run.completedAt, + logsDeletedAt: run.logsDeletedAt, + + environment: { + id: run.runtimeEnvironment.id, + organizationId: run.runtimeEnvironment.organizationId, + type: run.runtimeEnvironment.type, + slug: run.runtimeEnvironment.slug, + userId: run.runtimeEnvironment.orgMember?.user.id, + userName: getUsername(run.runtimeEnvironment.orgMember?.user), + }, + }, + }; + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.deployments/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.deployments/route.tsx index 0ec72c5744..a73f2c43b1 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.deployments/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.deployments/route.tsx @@ -2,16 +2,12 @@ import { ArrowPathIcon, ArrowUturnLeftIcon, BookOpenIcon, - CommandLineIcon, - ServerIcon, ServerStackIcon, } from "@heroicons/react/20/solid"; import { Outlet, useLocation, useParams } from "@remix-run/react"; import { LoaderFunctionArgs } from "@remix-run/server-runtime"; -import { Fragment } from "react/jsx-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; -import { BlankstateInstructions } from "~/components/BlankstateInstructions"; import { UserAvatar } from "~/components/UserProfilePhoto"; import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; @@ -104,104 +100,108 @@ export default function Page() { - - + + {hasDeployments ? ( -
- - - - Deploy - Env - Version - Status - Tasks - Deployed at - Deployed by - Go to page - - - - {deployments.length > 0 ? ( - deployments.map((deployment) => { - const usernameForEnv = - user.id !== deployment.environment.userId - ? deployment.environment.userName - : undefined; - const path = v3DeploymentPath( - organization, - project, - deployment, - currentPage - ); - return ( - - -
- {deployment.shortCode} - {deployment.label && ( - {deployment.label} - )} -
-
- - - - {deployment.version} - - - - - {deployment.tasksCount !== null ? deployment.tasksCount : "–"} - - - {deployment.deployedAt ? ( - - ) : ( - "–" - )} - - - {deployment.deployedBy ? ( -
- +
+
+
+ + + Deploy + Env + Version + Status + Tasks + Deployed at + Deployed by + Go to page + + + + {deployments.length > 0 ? ( + deployments.map((deployment) => { + const usernameForEnv = + user.id !== deployment.environment.userId + ? deployment.environment.userName + : undefined; + const path = v3DeploymentPath( + organization, + project, + deployment, + currentPage + ); + return ( + + +
- {deployment.deployedBy.name ?? - deployment.deployedBy.displayName} + {deployment.shortCode} + {deployment.label && ( + {deployment.label} + )}
- ) : ( - "–" - )} -
- -
- ); - }) - ) : ( - - - No deploys match your filters - - - )} -
-
-
- + + + + + {deployment.version} + + + + + {deployment.tasksCount !== null ? deployment.tasksCount : "–"} + + + {deployment.deployedAt ? ( + + ) : ( + "–" + )} + + + {deployment.deployedBy ? ( +
+ + + {deployment.deployedBy.name ?? + deployment.deployedBy.displayName} + +
+ ) : ( + "–" + )} +
+ + + ); + }) + ) : ( + + + No deploys match your filters + + + )} + + +
+ +
) : ( @@ -211,8 +211,8 @@ export default function Page() { {deploymentParam && ( <> - - + + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx index 5c16417646..11524ce988 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx @@ -66,7 +66,6 @@ import { useReplaceLocation } from "~/hooks/useReplaceLocation"; import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys"; import { useUser } from "~/hooks/useUser"; import { RunPresenter } from "~/presenters/v3/RunPresenter.server"; -import { getResizableRunSettings, setResizableRunSettings } from "~/services/resizablePanel"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; import { lerp } from "~/utils/lerp"; @@ -80,6 +79,37 @@ import { } from "~/utils/pathBuilder"; import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; +import { getResizableSnapshot } from "~/services/resizablePanel.server"; + +const resizableSettings = { + parent: { + autosaveId: "panel-run-parent", + handleId: "parent-handle", + main: { + id: "run", + min: "100px" as const, + }, + inspector: { + id: "inspector", + default: "430px" as const, + min: "50px" as const, + }, + }, + tree: { + autosaveId: "panel-run-tree", + handleId: "tree-handle", + tree: { + id: "tree", + default: "50%" as const, + min: "50px" as const, + }, + timeline: { + id: "timeline", + default: "50%" as const, + min: "50px" as const, + }, + }, +}; type TraceEvent = NonNullable["trace"]>["events"][0]; @@ -96,13 +126,17 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { }); //resizable settings - const resizeSettings = await getResizableRunSettings(request); + const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId); + const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId); return json({ run: result.run, trace: result.trace, maximumLiveReloadingSetting: env.MAXIMUM_LIVE_RELOADING_EVENTS, - resizeSettings, + resizable: { + parent, + tree, + }, }); }; @@ -114,8 +148,7 @@ function getSpanId(location: Location): string | undefined { } export default function Page() { - const { run, trace, resizeSettings, maximumLiveReloadingSetting } = - useLoaderData(); + const { run, trace, resizable, maximumLiveReloadingSetting } = useLoaderData(); const user = useUser(); const organization = useOrganization(); const project = useProject(); @@ -208,14 +241,14 @@ export default function Page() { run={run} trace={trace} maximumLiveReloadingSetting={maximumLiveReloadingSetting} - resizeSettings={resizeSettings} + resizable={resizable} /> ) : ( )}
@@ -223,7 +256,7 @@ export default function Page() { ); } -function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }: LoaderData) { +function TraceView({ run, trace, maximumLiveReloadingSetting, resizable }: LoaderData) { const organization = useOrganization(); const project = useProject(); const { location, replaceSearchParam } = useReplaceLocation(); @@ -255,15 +288,14 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }: return (
{ - if (layout.length !== 2) return; - if (!selectedSpanId) return; - setResizableRunSettings(document, layout); - }} > - + - + {selectedSpanId && ( - + + {" "} { - if (layout.length !== 2) return; - setResizableRunSettings(document, layout); - }} > - +
{daysSinceCompleted === undefined ? ( @@ -375,8 +413,14 @@ function NoLogsView({ run, resizeSettings }: LoaderData) { )}
- - + + + {" "}
@@ -464,15 +508,14 @@ function TasksTreeView({ />
- { - if (layout.length !== 2) return; - setResizableRunSettings(document, layout); - }} - > + {/* Tree list */} - +
{parentRunFriendlyId ? ( @@ -578,9 +621,13 @@ function TasksTreeView({ />
- + {/* Timeline */} - + { + const userId = await requireUserId(request); + const { projectParam, organizationSlug, runParam } = v3RunParamsSchema.parse(params); + + const presenter = new RunPresenter(); + const result = await presenter.call({ + userId, + organizationSlug, + projectSlug: projectParam, + runFriendlyId: runParam, + }); + + //resizable settings + const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId); + const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId); + + return typedjson({ + run: result.run, + resizable: { + parent, + tree, + }, + }); +}; + +type LoaderData = UseDataFunctionReturn; + +function getSpanId(location: Location): string | undefined { + const search = new URLSearchParams(location.search); + return search.get("span") ?? undefined; +} + +export default function Page() { + const { run, resizable } = useTypedLoaderData(); + + const user = useUser(); + const organization = useOrganization(); + const project = useProject(); + + const usernameForEnv = user.id !== run.environment.userId ? run.environment.userName : undefined; + + return ( + <> + + + Run #{run.number} + +
+ } + /> + + + + + ID + {run.id} + + + Trace ID + {run.traceId} + + + Env ID + {run.environment.id} + + + Org ID + {run.environment.organizationId} + + + + + + + + + + {run.isFinished ? null : ( + + + + + + + )} + + + +
+ }> + {() => } + +
+
+ + ); +} + +type InspectorState = + | { + type: "span"; + span?: TraceSpan; + } + | { + type: "run"; + run?: RawRun; + span?: TraceSpan; + } + | undefined; + +function Panels({ resizable, run: originalRun }: LoaderData) { + const { location, replaceSearchParam } = useReplaceLocation(); + const selectedSpanId = getSpanId(location); + + const appOrigin = useAppOrigin(); + const { isUpToDate, events, runs } = useSyncRunPage({ + origin: appOrigin, + traceId: originalRun.traceId, + }); + + const initialLoad = !isUpToDate || !runs; + + const trace = useMemo(() => { + if (!events) return undefined; + const preparedEvents = prepareTrace(events); + if (!preparedEvents) return undefined; + return createTraceTreeFromEvents(preparedEvents, originalRun.spanId); + }, [events, originalRun.spanId]); + + const inspectorState = useMemo(() => { + if (originalRun.logsDeletedAt) { + return { + type: "run", + run: runs?.find((r) => r.friendlyId === originalRun.friendlyId), + }; + } + + if (selectedSpanId) { + if (runs && runs.length > 0) { + const spanRun = runs.find((r) => r.spanId === selectedSpanId); + if (spanRun && events) { + const span = createSpanFromEvents(events, selectedSpanId); + return { + type: "run", + run: spanRun, + span, + }; + } + } + + if (!events) { + return { + type: "span", + span: undefined, + }; + } + + const span = createSpanFromEvents(events, selectedSpanId); + return { + type: "span", + span, + }; + } + }, [selectedSpanId, runs, events]); + + return ( + + + {initialLoad ? ( + + ) : ( + + )} + + + {inspectorState ? ( + + {inspectorState.type === "span" ? ( + replaceSearchParam("span") : undefined} + /> + ) : inspectorState.type === "run" ? ( + replaceSearchParam("span") : undefined} + /> + ) : null} + + ) : null} + + ); +} + +type TraceData = { + run: Run; + environmentType: RuntimeEnvironmentType; + trace?: Trace; + selectedSpanId: string | undefined; + replaceSearchParam: (key: string, value?: string) => void; +}; + +function TraceView({ run, environmentType, trace, selectedSpanId, replaceSearchParam }: TraceData) { + const changeToSpan = useDebounce((selectedSpan: string) => { + replaceSearchParam("span", selectedSpan); + }, 100); + + if (!trace) { + return ; + } + + const { events, parentRunFriendlyId, duration, rootSpanStatus, rootStartedAt } = trace; + + return ( + { + //instantly close the panel if no span is selected + if (!selectedSpan) { + replaceSearchParam("span"); + return; + } + + changeToSpan(selectedSpan); + }} + totalDuration={duration} + rootSpanStatus={rootSpanStatus} + rootStartedAt={rootStartedAt ? new Date(rootStartedAt) : undefined} + environmentType={environmentType} + /> + ); +} + +function NoLogsView({ run }: { run: Run }) { + const plan = useCurrentPlan(); + const organization = useOrganization(); + + const logRetention = plan?.v3Subscription?.plan?.limits.logRetentionDays.number ?? 30; + + const completedAt = run.completedAt ? new Date(run.completedAt) : undefined; + const now = new Date(); + + const daysSinceCompleted = completedAt + ? Math.floor((now.getTime() - completedAt.getTime()) / (1000 * 60 * 60 * 24)) + : undefined; + + const isWithinLogRetention = + daysSinceCompleted !== undefined && daysSinceCompleted <= logRetention; + + return ( +
+ {daysSinceCompleted === undefined ? ( + + + We tidy up older logs to keep things running smoothly. + + + ) : isWithinLogRetention ? ( + + + Your log retention is {logRetention} days but these logs had already been deleted. From + now on only logs from runs that completed {logRetention} days ago will be deleted. + + + ) : daysSinceCompleted <= 30 ? ( + + + The logs for this run have been deleted because the run completed {daysSinceCompleted}{" "} + days ago. + + Upgrade your plan to keep logs for longer. + + ) : ( + + + We tidy up older logs to keep things running smoothly. + + + )} +
+ ); +} + +type TasksTreeViewProps = { + events: TraceEvent[]; + selectedId?: string; + parentRunFriendlyId?: string; + onSelectedIdChanged: (selectedId: string | undefined) => void; + totalDuration: number; + rootSpanStatus: "executing" | "completed" | "failed"; + rootStartedAt: Date | undefined; + environmentType: RuntimeEnvironmentType; +}; + +function TasksTreeView({ + events, + selectedId, + parentRunFriendlyId, + onSelectedIdChanged, + totalDuration, + rootSpanStatus, + rootStartedAt, + environmentType, +}: TasksTreeViewProps) { + const [filterText, setFilterText] = useState(""); + const [errorsOnly, setErrorsOnly] = useState(false); + const [showDurations, setShowDurations] = useState(true); + const [scale, setScale] = useState(0); + const parentRef = useRef(null); + const treeScrollRef = useRef(null); + const timelineScrollRef = useRef(null); + + const { + nodes, + getTreeProps, + getNodeProps, + toggleNodeSelection, + toggleExpandNode, + expandAllBelowDepth, + toggleExpandLevel, + collapseAllBelowDepth, + selectNode, + scrollToNode, + virtualizer, + } = useTree({ + tree: events, + selectedId, + // collapsedIds, + onSelectedIdChanged, + estimatedRowHeight: () => 32, + parentRef, + filter: { + value: { text: filterText, errorsOnly }, + fn: (value, node) => { + const nodePassesErrorTest = (value.errorsOnly && node.data.isError) || !value.errorsOnly; + if (!nodePassesErrorTest) return false; + + if (value.text === "") return true; + if (node.data.message.toLowerCase().includes(value.text.toLowerCase())) { + return true; + } + return false; + }, + }, + }); + + return ( +
+
+ +
+ setErrorsOnly(e.valueOf())} + /> +
+
+ + {/* Tree list */} + +
+
+ {parentRunFriendlyId ? ( + + ) : ( + + This is the root task + + )} + +
+ ( + <> +
{ + selectNode(node.id); + }} + > +
+ {Array.from({ length: node.level }).map((_, index) => ( + + ))} +
{ + e.stopPropagation(); + if (e.altKey) { + if (state.expanded) { + collapseAllBelowDepth(node.level); + } else { + expandAllBelowDepth(node.level); + } + } else { + toggleExpandNode(node.id); + } + scrollToNode(node.id); + }} + > + {node.hasChildren ? ( + state.expanded ? ( + + ) : ( + + ) + ) : ( +
+ )} +
+
+ +
+
+ + + {node.data.isRoot && Root} +
+
+ +
+
+
+ {events.length === 1 && environmentType === "DEVELOPMENT" && ( + + )} + + )} + onScroll={(scrollTop) => { + //sync the scroll to the tree + if (timelineScrollRef.current) { + timelineScrollRef.current.scrollTop = scrollTop; + } + }} + /> +
+ + + {/* Timeline */} + + + + +
+
+
+ +
+
+ + Shortcuts + + Keyboard shortcuts +
+ +
+
+
+
+
+
+ setScale(value[0])} + min={0} + max={1} + step={0.05} + /> +
+
+
+ ); +} + +type TimelineViewProps = Pick< + TasksTreeViewProps, + "totalDuration" | "rootSpanStatus" | "events" | "rootStartedAt" +> & { + scale: number; + parentRef: React.RefObject; + timelineScrollRef: React.RefObject; + virtualizer: Virtualizer; + nodes: NodesState; + getNodeProps: UseTreeStateOutput["getNodeProps"]; + getTreeProps: UseTreeStateOutput["getTreeProps"]; + toggleNodeSelection: UseTreeStateOutput["toggleNodeSelection"]; + showDurations: boolean; + treeScrollRef: React.RefObject; +}; + +const tickCount = 5; + +function TimelineView({ + totalDuration, + scale, + rootSpanStatus, + rootStartedAt, + parentRef, + timelineScrollRef, + virtualizer, + events, + nodes, + getNodeProps, + getTreeProps, + toggleNodeSelection, + showDurations, + treeScrollRef, +}: TimelineViewProps) { + const timelineContainerRef = useRef(null); + const initialTimelineDimensions = useInitialDimensions(timelineContainerRef); + const minTimelineWidth = initialTimelineDimensions?.width ?? 300; + const maxTimelineWidth = minTimelineWidth * 10; + + //we want to live-update the duration if the root span is still executing + const [duration, setDuration] = useState(totalDuration); + useEffect(() => { + if (rootSpanStatus !== "executing" || !rootStartedAt) { + setDuration(totalDuration); + return; + } + + const interval = setInterval(() => { + setDuration(millisecondsToNanoseconds(Date.now() - rootStartedAt.getTime())); + }, 500); + + return () => clearInterval(interval); + }, [totalDuration, rootSpanStatus]); + + return ( +
+ + {/* Follows the cursor */} + + + + {/* The duration labels */} + + + + {(ms: number, index: number) => { + if (index === tickCount - 1) return null; + return ( + + {(ms) => ( +
+ {formatDurationMilliseconds(ms, { + style: "short", + maxDecimalPoints: ms < 1000 ? 0 : 1, + })} +
+ )} +
+ ); + }} +
+ {rootSpanStatus !== "executing" && ( + + {(ms) => ( +
+ {formatDurationMilliseconds(ms, { + style: "short", + maxDecimalPoints: ms < 1000 ? 0 : 1, + })} +
+ )} +
+ )} +
+ + + {(ms: number, index: number) => { + if (index === 0 || index === tickCount - 1) return null; + return ( + + ); + }} + + + +
+ {/* Main timeline body */} + + {/* The vertical tick lines */} + + {(ms: number, index: number) => { + if (index === 0) return null; + return ; + }} + + {/* The completed line */} + {rootSpanStatus !== "executing" && ( + + )} + { + return ( + console.log(`hover ${index}`)} + onClick={(e) => { + toggleNodeSelection(node.id); + }} + > + {node.data.level === "TRACE" ? ( + + ) : ( + + {(ms) => ( + + )} + + )} + + ); + }} + onScroll={(scrollTop) => { + //sync the scroll to the tree + if (treeScrollRef.current) { + treeScrollRef.current.scrollTop = scrollTop; + } + }} + /> + +
+
+
+ ); +} + +function NodeText({ node }: { node: TraceEvent }) { + const className = "truncate"; + return ( + + + + ); +} + +function NodeStatusIcon({ node }: { node: TraceEvent }) { + if (node.data.level !== "TRACE") return null; + if (node.data.style.variant !== "primary") return null; + + if (node.data.isCancelled) { + return ( + <> + + Canceled + + + + ); + } + + if (node.data.isError) { + return ; + } + + if (node.data.isPartial) { + return ; + } + + return ; +} + +function TaskLine({ isError, isSelected }: { isError: boolean; isSelected: boolean }) { + return
; +} + +function ShowParentLink({ runFriendlyId }: { runFriendlyId: string }) { + const [mouseOver, setMouseOver] = useState(false); + const organization = useOrganization(); + const project = useProject(); + const { spanParam } = useParams(); + + return ( + setMouseOver(true)} + onMouseLeave={() => setMouseOver(false)} + fullWidth + textAlignLeft + shortcut={{ key: "p" }} + className="flex-1" + > + {mouseOver ? ( + + ) : ( + + )} + + Show parent items + + + ); +} + +function LiveReloadingStatus({ rootSpanCompleted }: { rootSpanCompleted: boolean }) { + if (rootSpanCompleted) return null; + + return ( +
+ + + Live reloading + +
+ ); +} + +function PulsingDot() { + return ( + + + + + ); +} + +function SpanWithDuration({ + showDuration, + node, + ...props +}: Timeline.SpanProps & { node: TraceEvent; showDuration: boolean }) { + return ( + + + {node.data.isPartial && ( +
+ )} +
+
+ {formatDurationMilliseconds(props.durationMs, { + style: "short", + maxDecimalPoints: props.durationMs < 1000 ? 0 : 1, + })} +
+
+ + + ); +} + +const edgeBoundary = 0.05; + +function CurrentTimeIndicator({ totalDuration }: { totalDuration: number }) { + return ( + + {(ms) => { + const ratio = ms / nanosecondsToMilliseconds(totalDuration); + let offset = 0.5; + if (ratio < edgeBoundary) { + offset = lerp(0, 0.5, ratio / edgeBoundary); + } else if (ratio > 1 - edgeBoundary) { + offset = lerp(0.5, 1, (ratio - (1 - edgeBoundary)) / edgeBoundary); + } + + return ( +
+
+
+ {formatDurationMilliseconds(ms, { + style: "short", + maxDecimalPoints: ms < 1000 ? 0 : 1, + })} +
+
+
+
+ ); + }} + + ); +} + +function ConnectedDevWarning() { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => { + setIsVisible(true); + }, 6000); + + return () => clearTimeout(timer); + }, []); + + return ( +
+ +
+ + Runs usually start within 2 seconds in{" "} + . Check you're running the + CLI: npx trigger.dev@beta dev + +
+
+
+ ); +} + +function KeyboardShortcuts({ + expandAllBelowDepth, + collapseAllBelowDepth, + toggleExpandLevel, + setShowDurations, +}: { + expandAllBelowDepth: (depth: number) => void; + collapseAllBelowDepth: (depth: number) => void; + toggleExpandLevel: (depth: number) => void; + setShowDurations: (show: (show: boolean) => boolean) => void; +}) { + return ( + <> + + expandAllBelowDepth(0)} + title="Expand all" + /> + collapseAllBelowDepth(1)} + title="Collapse all" + /> + toggleExpandLevel(number)} /> + + ); +} + +function ArrowKeyShortcuts() { + return ( +
+ + + + + + Navigate + +
+ ); +} + +function ShortcutWithAction({ + shortcut, + title, + action, +}: { + shortcut: Shortcut; + title: string; + action: () => void; +}) { + useShortcutKeys({ + shortcut, + action, + }); + + return ( +
+ + + {title} + +
+ ); +} + +function NumberShortcuts({ toggleLevel }: { toggleLevel: (depth: number) => void }) { + useHotkeys(["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], (event, hotkeysEvent) => { + toggleLevel(Number(event.key)); + }); + + return ( +
+ 0 + + 9 + + Toggle level + +
+ ); +} + +function SearchField({ onChange }: { onChange: (value: string) => void }) { + const [value, setValue] = useState(""); + + const updateFilterText = useDebounce((text: string) => { + onChange(text); + }, 250); + + const updateValue = useCallback((value: string) => { + setValue(value); + updateFilterText(value); + }, []); + + return ( + updateValue(e.target.value)} + /> + ); +} + +export function Loading() { + return ( +
+
+ + + Loading logs + +
+
+
+ ); +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules/route.tsx index 53b4f31ece..70941ef5b8 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules/route.tsx @@ -177,8 +177,8 @@ export default function Page() { - - + +
{possibleTasks.length === 0 ? ( @@ -250,8 +250,8 @@ export default function Page() { {(isShowingNewPane || isShowingSchedule) && ( <> - - + + diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.test.tasks.$taskParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.test.tasks.$taskParam/route.tsx index 76cf92ecc9..f8fbde5bdd 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.test.tasks.$taskParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.test.tasks.$taskParam/route.tsx @@ -180,8 +180,8 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa onSubmit={(e) => submitForm(e)} > - - + +
- - + + - - + +
@@ -402,8 +402,8 @@ function ScheduledTaskForm({
- - + +
- - + +
@@ -117,8 +117,8 @@ export default function Page() {
) : hasSelectedEnvironment ? ( -
-
+
+
Select a task
{!rest.tasks?.length ? ( @@ -135,8 +135,8 @@ export default function Page() { )}
- - + + @@ -157,7 +157,7 @@ function TaskSelector({ return (
-
+
; + +export const loader = async ({ request, params }: LoaderFunctionArgs) => { + const userId = await requireUserId(request); + const parsedParams = v3RunParamsSchema.pick({ runParam: true }).parse(params); + + const run = await $replica.taskRun.findFirst({ + select: { + id: true, + traceId: true, + //metadata + number: true, + taskIdentifier: true, + friendlyId: true, + isTest: true, + tags: { + select: { + name: true, + }, + }, + machinePreset: true, + lockedToVersion: { + select: { + version: true, + sdkVersion: true, + }, + }, + //status + duration + status: true, + startedAt: true, + createdAt: true, + updatedAt: true, + queuedAt: true, + completedAt: true, + logsDeletedAt: true, + //idempotency + idempotencyKey: true, + //delayed + delayUntil: true, + //ttl + ttl: true, + expiredAt: true, + //queue + queue: true, + concurrencyKey: true, + //schedule + schedule: { + select: { + friendlyId: true, + generatorExpression: true, + timezone: true, + generatorDescription: true, + }, + }, + //usage + baseCostInCents: true, + costInCents: true, + usageDurationMs: true, + //env + runtimeEnvironment: { + select: { id: true, slug: true, type: true }, + }, + payload: true, + payloadType: true, + maxAttempts: true, + project: { + include: { + organization: true, + }, + }, + lockedBy: { + select: { + filePath: true, + exportName: true, + }, + }, + }, + where: { + friendlyId: parsedParams.runParam, + project: { + organization: { + members: { + some: { + userId, + }, + }, + }, + }, + }, + }); + + if (!run) { + throw new Response("Not found", { status: 404 }); + } + + const isFinished = isFinalRunStatus(run.status); + + const finishedAttempt = isFinished + ? await $replica.taskRunAttempt.findFirst({ + select: { + output: true, + outputType: true, + error: true, + }, + where: { + status: { in: FINAL_ATTEMPT_STATUSES }, + taskRunId: run.id, + }, + orderBy: { + createdAt: "desc", + }, + }) + : null; + + const output = + finishedAttempt === null + ? undefined + : finishedAttempt.outputType === "application/store" + ? `/resources/packets/${run.runtimeEnvironment.id}/${finishedAttempt.output}` + : typeof finishedAttempt.output !== "undefined" && finishedAttempt.output !== null + ? await prettyPrintPacket(finishedAttempt.output, finishedAttempt.outputType ?? undefined) + : undefined; + + const payload = + run.payloadType === "application/store" + ? `/resources/packets/${run.runtimeEnvironment.id}/${run.payload}` + : typeof run.payload !== "undefined" && run.payload !== null + ? await prettyPrintPacket(run.payload, run.payloadType ?? undefined) + : undefined; + + let error: TaskRunError | undefined = undefined; + if (finishedAttempt?.error) { + const result = TaskRunError.safeParse(finishedAttempt.error); + if (result.success) { + error = result.data; + } else { + error = { + type: "CUSTOM_ERROR", + raw: JSON.stringify(finishedAttempt.error), + }; + } + } + + const context = { + task: { + id: run.taskIdentifier, + filePath: run.lockedBy?.filePath, + exportName: run.lockedBy?.exportName, + }, + run: { + id: run.friendlyId, + createdAt: run.createdAt, + tags: run.tags.map((tag) => tag.name), + isTest: run.isTest, + idempotencyKey: run.idempotencyKey ?? undefined, + startedAt: run.startedAt ?? run.createdAt, + durationMs: run.usageDurationMs, + costInCents: run.costInCents, + baseCostInCents: run.baseCostInCents, + maxAttempts: run.maxAttempts ?? undefined, + version: run.lockedToVersion?.version, + }, + queue: { + name: run.queue, + }, + environment: { + id: run.runtimeEnvironment.id, + slug: run.runtimeEnvironment.slug, + type: run.runtimeEnvironment.type, + }, + organization: { + id: run.project.organization.id, + slug: run.project.organization.slug, + name: run.project.organization.title, + }, + project: { + id: run.project.id, + ref: run.project.externalRef, + slug: run.project.slug, + name: run.project.name, + }, + machine: run.machinePreset + ? machinePresetFromName(run.machinePreset as MachinePresetName) + : undefined, + }; + + return typedjson({ + friendlyId: run.friendlyId, + status: run.status, + createdAt: run.createdAt, + startedAt: run.startedAt, + updatedAt: run.updatedAt, + delayUntil: run.delayUntil, + expiredAt: run.expiredAt, + completedAt: run.completedAt, + logsDeletedAt: run.logsDeletedAt, + ttl: run.ttl, + taskIdentifier: run.taskIdentifier, + version: run.lockedToVersion?.version, + sdkVersion: run.lockedToVersion?.sdkVersion, + isTest: run.isTest, + environmentId: run.runtimeEnvironment.id, + schedule: run.schedule + ? { + friendlyId: run.schedule.friendlyId, + generatorExpression: run.schedule.generatorExpression, + description: run.schedule.generatorDescription, + timezone: run.schedule.timezone, + } + : undefined, + queue: { + name: run.queue, + isCustomQueue: !run.queue.startsWith("task/"), + concurrencyKey: run.concurrencyKey, + }, + tags: run.tags.map((tag) => tag.name), + baseCostInCents: run.baseCostInCents, + costInCents: run.costInCents, + totalCostInCents: run.costInCents + run.baseCostInCents, + usageDurationMs: run.usageDurationMs, + isFinished, + isRunning: RUNNING_STATUSES.includes(run.status), + payload, + payloadType: run.payloadType, + output, + outputType: finishedAttempt?.outputType ?? "application/json", + error, + context: JSON.stringify(context, null, 2), + }); +}; diff --git a/apps/webapp/app/routes/storybook.resizable/route.tsx b/apps/webapp/app/routes/storybook.resizable/route.tsx new file mode 100644 index 0000000000..109b7b5af1 --- /dev/null +++ b/apps/webapp/app/routes/storybook.resizable/route.tsx @@ -0,0 +1,24 @@ +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "~/components/primitives/Resizable"; + +export default function Story() { + return ( +
+ + + + + +
+ ); +} diff --git a/apps/webapp/app/routes/storybook/route.tsx b/apps/webapp/app/routes/storybook/route.tsx index ad8e61b0b4..b6ac587fff 100644 --- a/apps/webapp/app/routes/storybook/route.tsx +++ b/apps/webapp/app/routes/storybook/route.tsx @@ -72,6 +72,10 @@ const stories: Story[] = [ name: "Radio group", slug: "radio-group", }, + { + name: "Resizable", + slug: "resizable", + }, { name: "Segemented control", slug: "segmented-control", diff --git a/apps/webapp/app/routes/sync.traces.$traceId.ts b/apps/webapp/app/routes/sync.traces.$traceId.ts new file mode 100644 index 0000000000..1a06dd4885 --- /dev/null +++ b/apps/webapp/app/routes/sync.traces.$traceId.ts @@ -0,0 +1,69 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { $replica } from "~/db.server"; +import { env } from "~/env.server"; +import { logger } from "~/services/logger.server"; +import { getUserId } from "~/services/session.server"; +import { longPollingFetch } from "~/utils/longPollingFetch"; + +export async function loader({ params, request }: LoaderFunctionArgs) { + try { + const userId = await getUserId(request); + + logger.log(`/sync/traces/${params.traceId}`, { userId }); + + if (!userId) { + return new Response("No user found in cookie", { status: 401 }); + } + + const trace = await $replica.taskEvent.findFirst({ + select: { + organizationId: true, + }, + where: { + traceId: params.traceId, + }, + }); + + if (!trace) { + return new Response("No trace found", { status: 404 }); + } + + const member = await $replica.orgMember.findFirst({ + where: { + organizationId: trace.organizationId, + userId, + }, + }); + + if (!member) { + return new Response("Not a member of this org", { status: 401 }); + } + + const url = new URL(request.url); + const originUrl = new URL(`${env.ELECTRIC_ORIGIN}/v1/shape/public."TaskEvent"`); + url.searchParams.forEach((value, key) => { + originUrl.searchParams.set(key, value); + }); + + originUrl.searchParams.set("where", `"traceId"='${params.traceId}'`); + + const finalUrl = originUrl.toString(); + + logger.log("Fetching trace data", { url: finalUrl }); + + return longPollingFetch(finalUrl); + } catch (error) { + if (error instanceof Response) { + // Error responses from longPollingFetch + return error; + } else if (error instanceof TypeError) { + // Unexpected errors + logger.error("Unexpected error in loader:", { error: error.message }); + return new Response("An unexpected error occurred", { status: 500 }); + } else { + // Unknown errors + logger.error("Unknown error occurred in loader, not Error", { error: JSON.stringify(error) }); + return new Response("An unknown error occurred", { status: 500 }); + } + } +} diff --git a/apps/webapp/app/routes/sync.traces.runs.$traceId.ts b/apps/webapp/app/routes/sync.traces.runs.$traceId.ts new file mode 100644 index 0000000000..279e2ffa51 --- /dev/null +++ b/apps/webapp/app/routes/sync.traces.runs.$traceId.ts @@ -0,0 +1,79 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { z } from "zod"; +import { $replica } from "~/db.server"; +import { env } from "~/env.server"; +import { logger } from "~/services/logger.server"; +import { getUserId } from "~/services/session.server"; +import { longPollingFetch } from "~/utils/longPollingFetch"; + +const Params = z.object({ + traceId: z.string(), +}); + +export async function loader({ params, request }: LoaderFunctionArgs) { + try { + const userId = await getUserId(request); + const { traceId } = Params.parse(params); + + logger.log(`/sync/runs/${traceId}`, { userId }); + + if (!userId) { + return new Response("No user found in cookie", { status: 401 }); + } + + const run = await $replica.taskRun.findFirst({ + select: { + project: { + select: { + organizationId: true, + }, + }, + }, + where: { + traceId, + }, + }); + + if (!run) { + return new Response("No run found", { status: 404 }); + } + + const member = await $replica.orgMember.findFirst({ + where: { + organizationId: run.project.organizationId, + userId, + }, + }); + + if (!member) { + return new Response("Not a member of this org", { status: 401 }); + } + + const url = new URL(request.url); + const originUrl = new URL(`${env.ELECTRIC_ORIGIN}/v1/shape/public."TaskRun"`); + url.searchParams.forEach((value, key) => { + originUrl.searchParams.set(key, value); + }); + + originUrl.searchParams.set("where", `"traceId"='${traceId}'`); + + const finalUrl = originUrl.toString(); + + logger.log("Fetching trace runs data", { url: finalUrl }); + + return longPollingFetch(finalUrl); + } catch (error) { + if (error instanceof Response) { + // Error responses from longPollingFetch + return error; + } else if (error instanceof TypeError) { + // Unexpected errors + logger.error("Unexpected error in loader:", { error: error.message }); + return new Response("An unexpected error occurred", { status: 500 }); + } else { + // Unknown errors + logger.error("Unknown error occurred in loader, not Error", { error: JSON.stringify(error) }); + return new Response("An unknown error occurred", { status: 500 }); + } + } +} diff --git a/apps/webapp/app/services/resizablePanel.server.ts b/apps/webapp/app/services/resizablePanel.server.ts new file mode 100644 index 0000000000..f24e92770f --- /dev/null +++ b/apps/webapp/app/services/resizablePanel.server.ts @@ -0,0 +1,39 @@ +import { parse } from "cookie"; +import { type ResizableSnapshot } from "~/components/primitives/Resizable"; +import { logger } from "./logger.server"; + +export async function getResizableSnapshot( + request: Request, + id: string +): Promise { + try { + const cookieHeader = request.headers.get("Cookie"); + if (!cookieHeader) { + return undefined; + } + + const cookies = parse(cookieHeader); + const cookieValue = cookies[id]; + + if (cookieValue) { + try { + const parsedValue = JSON.parse(cookieValue) as any; + if (typeof parsedValue === "object" && "status" in parsedValue) { + return parsedValue as ResizableSnapshot; + } + } catch (parseError) { + logger.error("getResizableSnapshot() error parsing cookie value:", { + parseError, + cookieValue, + }); + } + } + + return undefined; + } catch (error) { + logger.error("getResizableSnapshot() error:", { + error, + }); + return undefined; + } +} diff --git a/apps/webapp/app/services/resizablePanel.ts b/apps/webapp/app/services/resizablePanel.ts deleted file mode 100644 index e3196a1c88..0000000000 --- a/apps/webapp/app/services/resizablePanel.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { z } from "zod"; - -const ResizableConfig = z - .object({ layout: z.array(z.number()).optional() }) - .default({ layout: undefined }); -type ResizableConfig = z.infer; - -function getCookieValue(cookieHeader: string | null, cookieName: string): ResizableConfig { - const cookieValue = cookieHeader?.split(`${cookieName}=`)[1]?.split(";")[0]; - if (!cookieValue) { - return { layout: undefined }; - } - try { - const json = JSON.parse(cookieValue); - return ResizableConfig.parse(json); - } catch (e) { - return { layout: undefined }; - } -} - -//run page -const runResizableName = "resizable-panels:run"; - -export async function getResizableRunSettings(request: Request): Promise { - const cookieHeader = request.headers.get("Cookie"); - return getCookieValue(cookieHeader, runResizableName); -} - -export async function setResizableRunSettings(document: Document, layout: number[]) { - document.cookie = `${runResizableName}=${JSON.stringify({ layout })}`; -} diff --git a/apps/webapp/app/utils/longPollingFetch.ts b/apps/webapp/app/utils/longPollingFetch.ts new file mode 100644 index 0000000000..7d183d3acb --- /dev/null +++ b/apps/webapp/app/utils/longPollingFetch.ts @@ -0,0 +1,46 @@ +// When proxying long-polling requests, content-encoding & content-length are added +// erroneously (saying the body is gzipped when it's not) so we'll just remove +// them to avoid content decoding errors in the browser. +// + +import { logger } from "~/services/logger.server"; + +// Similar-ish problem to https://github.com/wintercg/fetch/issues/23 +export async function longPollingFetch(url: string, options?: RequestInit) { + try { + let response = await fetch(url, options); + + // Check if the response is ok (status in the range 200-299) + if (!response.ok) { + const body = await response.text(); + throw new Error(`HTTP error! status: ${response.status}. ${body}`); + } + + if (response.headers.get(`content-encoding`)) { + const headers = new Headers(response.headers); + headers.delete(`content-encoding`); + headers.delete(`content-length`); + response = new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers, + }); + } + + return response; + } catch (error) { + if (error instanceof TypeError) { + // Network error or other fetch-related errors + logger.error("Network error:", { error: error.message }); + throw new Response("Network error occurred", { status: 503 }); + } else if (error instanceof Error) { + // HTTP errors or other known errors + logger.error("Fetch error:", { error: error.message }); + throw new Response(error.message, { status: 500 }); + } else { + // Unknown errors + logger.error("Unknown error occurred during fetch"); + throw new Response("An unknown error occurred", { status: 500 }); + } + } +} diff --git a/apps/webapp/app/utils/taskEvent.ts b/apps/webapp/app/utils/taskEvent.ts new file mode 100644 index 0000000000..41a1703c25 --- /dev/null +++ b/apps/webapp/app/utils/taskEvent.ts @@ -0,0 +1,574 @@ +import { Attributes, Link } from "@opentelemetry/api"; +import { + correctErrorStackTrace, + ExceptionEventProperties, + isExceptionSpanEvent, + millisecondsToNanoseconds, + NULL_SENTINEL, + SemanticInternalAttributes, + SpanEvent, + SpanEvents, + SpanMessagingEvent, + TaskEventStyle, + unflattenAttributes, +} from "@trigger.dev/core/v3"; +import { Prisma, TaskEvent } from "@trigger.dev/database"; +import { createTreeFromFlatItems, flattenTree } from "~/components/primitives/TreeView/TreeView"; +import type { + PreparedEvent, + SpanLink, + SpanSummary, + TraceSummary, +} from "~/v3/eventRepository.server"; + +export type TraceSpan = NonNullable>; + +export function prepareTrace(events: TaskEvent[]): TraceSummary | undefined { + let preparedEvents: Array = []; + let rootSpanId: string | undefined; + const eventsBySpanId = new Map(); + + for (const event of events) { + preparedEvents.push(prepareEvent(event)); + + if (!rootSpanId && !event.parentId) { + rootSpanId = event.spanId; + } + } + + for (const event of preparedEvents) { + const existingEvent = eventsBySpanId.get(event.spanId); + + if (!existingEvent) { + eventsBySpanId.set(event.spanId, event); + continue; + } + + if (event.isCancelled || !event.isPartial) { + eventsBySpanId.set(event.spanId, event); + } + } + + preparedEvents = Array.from(eventsBySpanId.values()); + + const spansBySpanId = new Map(); + + const spans = preparedEvents.map((event) => { + const ancestorCancelled = isAncestorCancelled(eventsBySpanId, event.spanId); + const duration = calculateDurationIfAncestorIsCancelled( + eventsBySpanId, + event.spanId, + event.duration + ); + + const span = { + recordId: event.id, + id: event.spanId, + parentId: event.parentId ?? undefined, + runId: event.runId, + idempotencyKey: event.idempotencyKey, + data: { + message: event.message, + style: event.style, + duration, + isError: event.isError, + isPartial: ancestorCancelled ? false : event.isPartial, + isCancelled: event.isCancelled === true ? true : event.isPartial && ancestorCancelled, + startTime: getDateFromNanoseconds(event.startTime), + level: event.level, + events: event.events, + environmentType: event.environmentType, + }, + }; + + spansBySpanId.set(event.spanId, span); + + return span; + }); + + if (!rootSpanId) { + return; + } + + const rootSpan = spansBySpanId.get(rootSpanId); + + if (!rootSpan) { + return; + } + + return { + rootSpan, + spans, + }; +} + +export function createTraceTreeFromEvents(traceSummary: TraceSummary, spanId: string) { + //this tree starts at the passed in span (hides parent elements if there are any) + const tree = createTreeFromFlatItems(traceSummary.spans, spanId); + + //we need the start offset for each item, and the total duration of the entire tree + const treeRootStartTimeMs = tree ? tree?.data.startTime.getTime() : 0; + let totalDuration = tree?.data.duration ?? 0; + const events = tree + ? flattenTree(tree).map((n) => { + const offset = millisecondsToNanoseconds(n.data.startTime.getTime() - treeRootStartTimeMs); + totalDuration = Math.max(totalDuration, offset + n.data.duration); + return { + ...n, + data: { + ...n.data, + //set partial nodes to null duration + duration: n.data.isPartial ? null : n.data.duration, + offset, + isRoot: n.id === traceSummary.rootSpan.id, + }, + }; + }) + : []; + + //total duration should be a minimum of 1ms + totalDuration = Math.max(totalDuration, millisecondsToNanoseconds(1)); + + let rootSpanStatus: "executing" | "completed" | "failed" = "executing"; + if (events[0]) { + if (events[0].data.isError) { + rootSpanStatus = "failed"; + } else if (!events[0].data.isPartial) { + rootSpanStatus = "completed"; + } + } + + return { + rootSpanStatus, + events: events, + parentRunFriendlyId: + tree?.id === traceSummary.rootSpan.id ? undefined : traceSummary.rootSpan.runId, + duration: totalDuration, + rootStartedAt: tree?.data.startTime, + }; +} + +export function createSpanFromEvents(events: TaskEvent[], spanId: string) { + const spanEvent = getSpanEvent(events, spanId); + + if (!spanEvent) { + return; + } + + const preparedEvent = prepareEvent(spanEvent); + const span = createSpanFromEvent(events, preparedEvent); + + const output = rehydrateJson(spanEvent.output); + const payload = rehydrateJson(spanEvent.payload); + + const show = rehydrateShow(spanEvent.properties); + + const properties = sanitizedAttributes(spanEvent.properties); + + const messagingEvent = SpanMessagingEvent.optional().safeParse((properties as any)?.messaging); + + const links: SpanLink[] = []; + + if (messagingEvent.success && messagingEvent.data) { + if (messagingEvent.data.message && "id" in messagingEvent.data.message) { + if (messagingEvent.data.message.id.startsWith("run_")) { + links.push({ + type: "run", + icon: "runs", + title: `Run ${messagingEvent.data.message.id}`, + runId: messagingEvent.data.message.id, + }); + } + } + } + + const backLinks = spanEvent.links as any as Link[] | undefined; + + if (backLinks && backLinks.length > 0) { + backLinks.forEach((l) => { + const title = String(l.attributes?.[SemanticInternalAttributes.LINK_TITLE] ?? "Triggered by"); + + links.push({ + type: "span", + icon: "trigger", + title, + traceId: l.context.traceId, + spanId: l.context.spanId, + }); + }); + } + + const spanEvents = transformEvents( + preparedEvent.events, + spanEvent.metadata as Attributes, + spanEvent.environmentType === "DEVELOPMENT" + ); + + return { + ...spanEvent, + ...span.data, + payload, + output, + events: spanEvents, + show, + links, + properties: properties ? JSON.stringify(properties, null, 2) : undefined, + showActionBar: show?.actions === true, + }; +} + +export function createSpanFromEvent(events: TaskEvent[], event: PreparedEvent) { + let ancestorCancelled = false; + let duration = event.duration; + + if (!event.isCancelled && event.isPartial) { + walkSpanAncestors(events, event, (ancestorEvent, level) => { + if (level >= 8) { + return { stop: true }; + } + + if (ancestorEvent.isCancelled) { + ancestorCancelled = true; + + // We need to get the cancellation time from the cancellation span event + const cancellationEvent = ancestorEvent.events.find( + (event) => event.name === "cancellation" + ); + + if (cancellationEvent) { + duration = calculateDurationFromStart(event.startTime, cancellationEvent.time); + } + + return { stop: true }; + } + + return { stop: false }; + }); + } + + const span = { + recordId: event.id, + id: event.spanId, + parentId: event.parentId ?? undefined, + runId: event.runId, + idempotencyKey: event.idempotencyKey, + data: { + message: event.message, + style: event.style, + duration, + isError: event.isError, + isPartial: ancestorCancelled ? false : event.isPartial, + isCancelled: event.isCancelled === true ? true : event.isPartial && ancestorCancelled, + startTime: getDateFromNanoseconds(event.startTime), + level: event.level, + events: event.events, + environmentType: event.environmentType, + }, + }; + + return span; +} + +function walkSpanAncestors( + events: TaskEvent[], + event: PreparedEvent, + callback: (event: PreparedEvent, level: number) => { stop: boolean } +) { + const parentId = event.parentId; + if (!parentId) { + return; + } + + let parentEvent = getSpanEvent(events, parentId); + let level = 1; + + while (parentEvent) { + const preparedParentEvent = prepareEvent(parentEvent); + + const result = callback(preparedParentEvent, level); + + if (result.stop) { + return; + } + + if (!preparedParentEvent.parentId) { + return; + } + + parentEvent = getSpanEvent(events, preparedParentEvent.parentId); + + level++; + } +} + +function getSpanEvent(events: TaskEvent[], spanId: string) { + const spans = events.filter((e) => e.spanId === spanId); + const completedSpan = spans.find((s) => !s.isPartial); + + if (completedSpan) { + return completedSpan; + } + + return spans.at(0); +} + +export function prepareEvent(event: TaskEvent): PreparedEvent { + return { + ...event, + duration: Number(event.duration), + events: parseEventsField(event.events), + style: parseStyleField(event.style), + }; +} + +function parseEventsField(events: Prisma.JsonValue): SpanEvents { + const unsafe = events + ? (events as any[]).map((e) => ({ + ...e, + properties: unflattenAttributes(e.properties as Attributes), + })) + : undefined; + + return unsafe as SpanEvents; +} + +function parseStyleField(style: Prisma.JsonValue): TaskEventStyle { + const unsafe = unflattenAttributes(style as Attributes); + + if (!unsafe) { + return {}; + } + + if (typeof unsafe === "object") { + return Object.assign( + { + icon: undefined, + variant: undefined, + }, + unsafe + ) as TaskEventStyle; + } + + return {}; +} + +export function isAncestorCancelled(events: Map, spanId: string) { + const event = events.get(spanId); + + if (!event) { + return false; + } + + if (event.isCancelled) { + return true; + } + + if (event.parentId) { + return isAncestorCancelled(events, event.parentId); + } + + return false; +} + +function calculateDurationIfAncestorIsCancelled( + events: Map, + spanId: string, + defaultDuration: number +) { + const event = events.get(spanId); + + if (!event) { + return defaultDuration; + } + + if (event.isCancelled) { + return defaultDuration; + } + + if (!event.isPartial) { + return defaultDuration; + } + + if (event.parentId) { + const cancelledAncestor = findFirstCancelledAncestor(events, event.parentId); + + if (cancelledAncestor) { + // We need to get the cancellation time from the cancellation span event + const cancellationEvent = cancelledAncestor.events.find( + (event) => event.name === "cancellation" + ); + + if (cancellationEvent) { + return calculateDurationFromStart(event.startTime, cancellationEvent.time); + } + } + } + + return defaultDuration; +} + +export function calculateDurationFromStart(startTime: bigint, endTime: Date = new Date()) { + const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime; + + return Number(BigInt($endtime.getTime() * 1_000_000) - startTime); +} + +function findFirstCancelledAncestor(events: Map, spanId: string) { + const event = events.get(spanId); + + if (!event) { + return; + } + + if (event.isCancelled) { + return event; + } + + if (event.parentId) { + return findFirstCancelledAncestor(events, event.parentId); + } + + return; +} + +export function getDateFromNanoseconds(nanoseconds: bigint) { + return new Date(Number(nanoseconds) / 1_000_000); +} + +export function getNowInNanoseconds(): bigint { + return BigInt(new Date().getTime() * 1_000_000); +} + +export function rehydrateJson(json: Prisma.JsonValue): any { + if (json === null) { + return undefined; + } + + if (json === NULL_SENTINEL) { + return null; + } + + if (typeof json === "string") { + return json; + } + + if (typeof json === "number") { + return json; + } + + if (typeof json === "boolean") { + return json; + } + + if (Array.isArray(json)) { + return json.map((item) => rehydrateJson(item)); + } + + if (typeof json === "object") { + return unflattenAttributes(json as Attributes); + } + + return null; +} + +export function rehydrateShow(properties: Prisma.JsonValue): { actions?: boolean } | undefined { + if (properties === null || properties === undefined) { + return; + } + + if (typeof properties !== "object") { + return; + } + + if (Array.isArray(properties)) { + return; + } + + const actions = properties[SemanticInternalAttributes.SHOW_ACTIONS]; + + if (typeof actions === "boolean") { + return { actions }; + } + + return; +} + +export function sanitizedAttributes(json: Prisma.JsonValue) { + if (json === null || json === undefined) { + return; + } + + const withoutPrivateProperties = removePrivateProperties(json as Attributes); + if (!withoutPrivateProperties) { + return; + } + + return unflattenAttributes(withoutPrivateProperties); +} +// removes keys that start with a $ sign. If there are no keys left, return undefined +function removePrivateProperties( + attributes: Attributes | undefined | null +): Attributes | undefined { + if (!attributes) { + return undefined; + } + + const result: Attributes = {}; + + for (const [key, value] of Object.entries(attributes)) { + if (key.startsWith("$")) { + continue; + } + + result[key] = value; + } + + if (Object.keys(result).length === 0) { + return undefined; + } + + return result; +} + +export function transformEvents( + events: SpanEvents, + properties: Attributes, + isDev: boolean +): SpanEvents { + return (events ?? []).map((event) => transformEvent(event, properties, isDev)); +} + +function transformEvent(event: SpanEvent, properties: Attributes, isDev: boolean): SpanEvent { + if (isExceptionSpanEvent(event)) { + return { + ...event, + properties: { + exception: transformException(event.properties.exception, properties, isDev), + }, + }; + } + + return event; +} + +function transformException( + exception: ExceptionEventProperties, + properties: Attributes, + isDev: boolean +): ExceptionEventProperties { + const projectDirAttributeValue = properties[SemanticInternalAttributes.PROJECT_DIR]; + + if (projectDirAttributeValue !== undefined && typeof projectDirAttributeValue !== "string") { + return exception; + } + + return { + ...exception, + stacktrace: exception.stacktrace + ? correctErrorStackTrace(exception.stacktrace, projectDirAttributeValue, { + removeFirstLine: true, + isDev, + }) + : undefined, + }; +} diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 49e0edebdc..7315e294bc 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -46,6 +46,7 @@ "@conform-to/react": "^0.6.1", "@conform-to/zod": "^0.6.1", "@depot/sdk-node": "^1.0.0", + "@electric-sql/react": "^0.3.5", "@headlessui/react": "^1.7.8", "@heroicons/react": "^2.0.12", "@internationalized/date": "^3.5.1", @@ -107,6 +108,7 @@ "class-variance-authority": "^0.5.2", "clsx": "^1.2.1", "compression": "^1.7.4", + "cookie": "^0.6.0", "cron-parser": "^4.9.0", "cronstrue": "^2.21.0", "cross-env": "^7.0.3", @@ -150,6 +152,7 @@ "react-resizable-panels": "^2.0.9", "react-stately": "^3.29.1", "react-use": "^17.4.0", + "react-window-splitter": "^0.4.1", "recharts": "^2.12.6", "regression": "^2.0.1", "remix-auth": "^3.6.0", @@ -190,6 +193,7 @@ "@total-typescript/ts-reset": "^0.4.2", "@types/bcryptjs": "^2.4.2", "@types/compression": "^1.7.2", + "@types/cookie": "^0.6.0", "@types/eslint": "^8.4.6", "@types/express": "^4.17.13", "@types/humanize-duration": "^3.27.1", @@ -243,4 +247,4 @@ "engines": { "node": ">=16.0.0" } -} \ No newline at end of file +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 022ce47bc1..8595f2ae13 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -29,6 +29,7 @@ services: - listen_addresses=* - -c - wal_level=logical + pgadmin: container_name: pgadmin image: dpage/pgadmin4:8 @@ -58,6 +59,18 @@ services: ports: - 6379:6379 + electric: + image: electricsql/electric + restart: always + environment: + DATABASE_URL: postgresql://postgres:postgres@database:5432/postgres + networks: + - app_network + ports: + - "3060:3000" + depends_on: + - database + # otel-collector: # container_name: otel-collector # image: otel/opentelemetry-collector-contrib:latest diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7dd5b73311..54305948e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -231,6 +231,9 @@ importers: '@depot/sdk-node': specifier: ^1.0.0 version: 1.0.0 + '@electric-sql/react': + specifier: ^0.3.5 + version: 0.3.5(react@18.2.0) '@headlessui/react': specifier: ^1.7.8 version: 1.7.8(react-dom@18.2.0)(react@18.2.0) @@ -414,6 +417,9 @@ importers: compression: specifier: ^1.7.4 version: 1.7.4 + cookie: + specifier: ^0.6.0 + version: 0.6.0 cron-parser: specifier: ^4.9.0 version: 4.9.0 @@ -543,6 +549,9 @@ importers: react-use: specifier: ^17.4.0 version: 17.4.0(react-dom@18.2.0)(react@18.2.0) + react-window-splitter: + specifier: ^0.4.1 + version: 0.4.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0) recharts: specifier: ^2.12.6 version: 2.12.6(react-dom@18.2.0)(react@18.2.0) @@ -658,6 +667,9 @@ importers: '@types/compression': specifier: ^1.7.2 version: 1.7.2 + '@types/cookie': + specifier: ^0.6.0 + version: 0.6.0 '@types/eslint': specifier: ^8.4.6 version: 8.4.10 @@ -1526,6 +1538,10 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + /@adobe/css-tools@4.4.0: + resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} + dev: false + /@ai-sdk/provider-utils@1.0.17(zod@3.22.3): resolution: {integrity: sha512-2VyeTH5DQ6AxqvwdyytKIeiZyYTyJffpufWjE67zM2sXMIHgYl7fivo8m5wVl6Cbf1dFPSGKq//C9s+lz+NHrQ==} engines: {node: '>=18'} @@ -4453,6 +4469,25 @@ packages: '@connectrpc/connect-node': 1.4.0(@bufbuild/protobuf@1.10.0)(@connectrpc/connect@1.4.0) dev: false + /@electric-sql/client@0.4.0: + resolution: {integrity: sha512-YVYSqHitqVIDC1RBTfmHMfAfqDNAKMK9/AFVTDFQQxN3Q85dIQS49zThAuJVecYiuYRJvTiqf40c4n39jZSNrQ==} + optionalDependencies: + '@rollup/rollup-darwin-arm64': 4.21.3 + dev: false + + /@electric-sql/react@0.3.5(react@18.2.0): + resolution: {integrity: sha512-qPrlF3BsRg5L8zAn1sLGzc3pkswfEHyQI3lNOu7Xllv1DBx85RvHR1zgGGPAUfC8iwyWupQu9pFPE63GdbeuhA==} + peerDependencies: + react: ^18.3.1 + peerDependenciesMeta: + react: + optional: true + dependencies: + '@electric-sql/client': 0.4.0 + react: 18.2.0 + use-sync-external-store: 1.2.2(react@18.2.0) + dev: false + /@emotion/hash@0.9.0: resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} dev: true @@ -6513,6 +6548,12 @@ packages: '@swc/helpers': 0.5.2 dev: false + /@internationalized/date@3.5.5: + resolution: {integrity: sha512-H+CfYvOZ0LTJeeLOqm19E3uj/4YjrmOFtBufDHPfvtI80hFAMqtrp7oCACpe4Cil5l8S0Qu/9dYfZc/5lY8WQQ==} + dependencies: + '@swc/helpers': 0.5.5 + dev: false + /@internationalized/message@3.1.1: resolution: {integrity: sha512-ZgHxf5HAPIaR0th+w0RUD62yF6vxitjlprSxmLJ1tam7FOekqRSDELMg4Cr/DdszG5YLsp5BG3FgHgqquQZbqw==} dependencies: @@ -6520,18 +6561,37 @@ packages: intl-messageformat: 10.5.8 dev: false + /@internationalized/message@3.1.4: + resolution: {integrity: sha512-Dygi9hH1s7V9nha07pggCkvmRfDd3q2lWnMGvrJyrOwYMe1yj4D2T9BoH9I6MGR7xz0biQrtLPsqUkqXzIrBOw==} + dependencies: + '@swc/helpers': 0.5.5 + intl-messageformat: 10.5.8 + dev: false + /@internationalized/number@3.5.0: resolution: {integrity: sha512-ZY1BW8HT9WKYvaubbuqXbbDdHhOUMfE2zHHFJeTppid0S+pc8HtdIxFxaYMsGjCb4UsF+MEJ4n2TfU7iHnUK8w==} dependencies: '@swc/helpers': 0.5.2 dev: false + /@internationalized/number@3.5.3: + resolution: {integrity: sha512-rd1wA3ebzlp0Mehj5YTuTI50AQEx80gWFyHcQu+u91/5NgdwBecO8BH6ipPfE+lmQ9d63vpB3H9SHoIUiupllw==} + dependencies: + '@swc/helpers': 0.5.5 + dev: false + /@internationalized/string@3.2.0: resolution: {integrity: sha512-Xx3Sy3f2c9ctT+vh8c7euEaEHQZltp0euZ3Hy4UfT3E13r6lxpUS3kgKyumEjboJZSnaZv7JhqWz3D75v+IxQg==} dependencies: '@swc/helpers': 0.5.2 dev: false + /@internationalized/string@3.2.3: + resolution: {integrity: sha512-9kpfLoA8HegiWTeCbR2livhdVeKobCnVv8tlJ6M2jF+4tcMqDo94ezwlnrUANBWPgd8U7OXIHCk2Ov2qhk4KXw==} + dependencies: + '@swc/helpers': 0.5.5 + dev: false + /@ioredis/commands@1.2.0: resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} dev: false @@ -8362,6 +8422,19 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-compose-refs@1.1.0(@types/react@18.2.69)(react@18.2.0): + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.69 + react: 18.2.0 + dev: false + /@radix-ui/react-context@1.0.0(react@18.2.0): resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} peerDependencies: @@ -9722,6 +9795,20 @@ packages: '@babel/runtime': 7.24.5 dev: false + /@react-aria/breadcrumbs@3.5.16(react@18.2.0): + resolution: {integrity: sha512-OXLKKu4SmjnSaSHkk4kow5/aH/SzlHWPJt+Uq3xec9TwDOr/Ob8aeFVGFoY0HxfGozuQlUz+4e+d29vfA0jNWg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/link': 3.7.4(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/breadcrumbs': 3.7.7(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/breadcrumbs@3.5.9(react@18.2.0): resolution: {integrity: sha512-asbXTL5NjeHl1+YIF0K70y8tNHk8Lb6VneYH8yOkpLO49ejyNDYBK0tp0jtI9IZAQiTa2qkhYq58c9LloTwebQ==} peerDependencies: @@ -9751,6 +9838,41 @@ packages: react: 18.2.0 dev: false + /@react-aria/button@3.9.8(react@18.2.0): + resolution: {integrity: sha512-MdbMQ3t5KSCkvKtwYd/Z6sgw0v+r1VQFRYOZ4L53xOkn+u140z8vBpNeWKZh/45gxGv7SJn9s2KstLPdCWmIxw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/toggle': 3.7.7(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-aria/calendar@3.5.11(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-VLhBovLVu3uJXBkHbgEippmo/K58QLcc/tSJQ0aJUNyHsrvPgHEcj484cb+Uj/yOirXEIzaoW6WEvhcdKrb49Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/calendar': 3.5.4(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/calendar': 3.4.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/calendar@3.5.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-8k7khgea5kwfWriZJWCADNB0R2d7g5A6tTjUEktK4FFZcTb0RCubFejts4hRyzKlF9XHUro2dfh6sbZrzfMKDQ==} peerDependencies: @@ -9789,6 +9911,50 @@ packages: react: 18.2.0 dev: false + /@react-aria/checkbox@3.14.6(react@18.2.0): + resolution: {integrity: sha512-LICY1PR3WsW/VbuLMjZbxo75+poeo3XCXGcUnk6hxMlWfp/Iy/XHVsHlGu9stRPKRF8BSuOGteaHWVn6IXfwtA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/form': 3.0.8(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/toggle': 3.10.7(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/checkbox': 3.6.8(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/toggle': 3.7.7(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-aria/combobox@3.10.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-EdDwr2Rp1xy7yWjOYHt2qF1IpAtUrkaNKZJzlIw1XSwcqizQY6E8orNPdZr6ZwD6/tgujxF1N71JTKyffrR0Xw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/listbox': 3.13.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/menu': 3.15.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/overlays': 3.23.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/textfield': 3.14.8(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/combobox': 3.9.2(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/combobox': 3.12.1(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/combobox@3.8.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-q8Kdw1mx6nSSydXqRagRuyKH1NPGvpSOFjUfgxdO8ZqaEEuZX3ObOoiO/DLtXDndViNc03dMbMpfuJoLYXfCtg==} peerDependencies: @@ -9814,6 +9980,34 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/datepicker@3.11.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-6sbLln3VXSBcBRDgSACBzIzF/5KV5NlNOhZvXPFE6KqFw6GbevjZQTv5BNDXiwA3CQoawIRF7zgRvTANw8HkNA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@internationalized/number': 3.5.3 + '@internationalized/string': 3.2.3 + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/form': 3.0.8(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/spinbutton': 3.6.8(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/datepicker': 3.10.2(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/calendar': 3.4.9(react@18.2.0) + '@react-types/datepicker': 3.8.2(react@18.2.0) + '@react-types/dialog': 3.5.12(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/datepicker@3.9.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-bdlY2H/zwe3hQf64Lp1oGTf7Va8ennDyAv4Ffowb+BOoL8+FB9smtGyONKe87zXu7VJL2M5xYAi4n7c004PM+w==} peerDependencies: @@ -9858,6 +10052,22 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/dialog@3.5.17(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lvfEgaqg922J1hurscqCS600OZQVitGtdpo81kAefJaUzMnCxzrYviyT96aaW0simHOlimbYF5js8lxBLZJRaw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/overlays': 3.23.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/dialog': 3.5.12(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/dnd@3.5.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-7OPGePdle+xNYHAIAUOvIETRMfnkRt7h/C0bCkxUR2GYefEbTzfraso4ppNH2JZ7fCRd0K/Qe+jvQklwusHAKA==} peerDependencies: @@ -9878,6 +10088,26 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/dnd@3.7.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-NuE3EGqoBbe9aXAO9mDfbu4kMO7S4MCgkjkCqYi16TWfRUf38ajQbIlqodCx91b3LVN3SYvNbE3D4Tj5ebkljw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/string': 3.2.3 + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/overlays': 3.23.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/dnd': 3.4.2(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/focus@3.16.0(react@18.2.0): resolution: {integrity: sha512-GP6EYI07E8NKQQcXHjpIocEU0vh0oi0Vcsd+/71fKS0NnTR0TUOEeil0JuuQ9ymkmPDTu51Aaaa4FxVsuN/23A==} peerDependencies: @@ -9891,6 +10121,19 @@ packages: react: 18.2.0 dev: false + /@react-aria/focus@3.18.2(react@18.2.0): + resolution: {integrity: sha512-Jc/IY+StjA3uqN73o6txKQ527RFU7gnG5crEl5Xy3V+gbYp2O5L3ezAo/E0Ipi2cyMbG6T5Iit1IDs7hcGu8aw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + clsx: 2.1.0 + react: 18.2.0 + dev: false + /@react-aria/form@3.0.1(react@18.2.0): resolution: {integrity: sha512-6586oODMDR4/ciGRwXjpvEAg7tWGSDrXE//waK0n5e5sMuzlPOo1DHc5SpPTvz0XdJsu6VDt2rHdVWVIC9LEyw==} peerDependencies: @@ -9904,6 +10147,42 @@ packages: react: 18.2.0 dev: false + /@react-aria/form@3.0.8(react@18.2.0): + resolution: {integrity: sha512-8S2QiyUdAgK43M3flohI0R+2rTyzH088EmgeRArA8euvJTL16cj/oSOKMEgWVihjotJ9n6awPb43ZhKboyNsMg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-aria/grid@3.10.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-l0r9mz05Gwjq3t6JOTNQOf+oAoWN0bXELPJtIr8m0XyXMPFCQe1xsTaX8igVQdrDmXyBc75RAWS0BJo2JF2fIA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/grid': 3.9.2(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@react-types/grid': 3.2.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/grid@3.8.6(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JlQDkdm5heG1FfRyy5KnB8b6s/hRqSI6Xt2xN2AccLX5kcbfFr2/d5KVxyf6ahfa4Gfd46alN6477ju5eTWJew==} peerDependencies: @@ -9947,6 +10226,27 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/gridlist@3.9.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-bb9GnKKeuL6NljoVUcHxr9F0cy/2WDOXRYeMikTnviRw6cuX95oojrhFfCUvz2d6ID22Btrvh7LkE+oIPVuc+g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/grid': 3.10.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-stately/tree': 3.8.4(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/i18n@3.10.0(react@18.2.0): resolution: {integrity: sha512-sviD5Y1pLPG49HHRmVjR+5nONrp0HK219+nu9Y7cDfUhXu2EjyhMS9t/n9/VZ69hHChZ2PnHYLEE2visu9CuCg==} peerDependencies: @@ -9963,6 +10263,22 @@ packages: react: 18.2.0 dev: false + /@react-aria/i18n@3.12.2(react@18.2.0): + resolution: {integrity: sha512-PvEyC6JWylTpe8dQEWqQwV6GiA+pbTxHQd//BxtMSapRW3JT9obObAnb/nFhj3HthkUvqHyj0oO1bfeN+mtD8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@internationalized/message': 3.1.4 + '@internationalized/number': 3.5.3 + '@internationalized/string': 3.2.3 + '@react-aria/ssr': 3.9.5(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/interactions@3.20.1(react@18.2.0): resolution: {integrity: sha512-PLNBr87+SzRhe9PvvF9qvzYeP4ofTwfKSorwmO+hjr3qoczrSXf4LRQlb27wB6hF10C7ZE/XVbUI1lj4QQrZ/g==} peerDependencies: @@ -9975,6 +10291,29 @@ packages: react: 18.2.0 dev: false + /@react-aria/interactions@3.22.2(react@18.2.0): + resolution: {integrity: sha512-xE/77fRVSlqHp2sfkrMeNLrqf2amF/RyuAS6T5oDJemRSgYM3UoxTbWjucPhfnoW7r32pFPHHgz4lbdX8xqD/g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/ssr': 3.9.5(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-aria/label@3.7.11(react@18.2.0): + resolution: {integrity: sha512-REgejE5Qr8cXG/b8H2GhzQmjQlII/0xQW/4eDzydskaTLvA7lF5HoJUE6biYTquH5va38d8XlH465RPk+bvHzA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/label@3.7.4(react@18.2.0): resolution: {integrity: sha512-3Y0yyrqpLzZdzHw+TOyzwuyx5wa2ujU5DGfKuL5GFnU9Ii4DtdwBGSYS7Yu7qadU+eQmG4OGhAgFVswbIgIwJw==} peerDependencies: @@ -10000,6 +10339,20 @@ packages: react: 18.2.0 dev: false + /@react-aria/link@3.7.4(react@18.2.0): + resolution: {integrity: sha512-E8SLDuS9ssm/d42+3sDFNthfMcNXMUrT2Tq1DIZt22EsMcuEzmJ9B0P7bDP5RgvIw05xVGqZ20nOpU4mKTxQtA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/link': 3.5.7(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/listbox@3.11.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-PBrnldmyEYUUJvfDeljW8ITvZyBTfGpLNf0b5kfBPK3TDgRH4niEH2vYEcaZvSqb0FrpdvcunuTRXcOpfb+gCQ==} peerDependencies: @@ -10019,12 +10372,37 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/listbox@3.13.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-htluPyDfFtn66OEYaJdIaFCYH9wGCNk30vOgZrQkPul9F9Cjce52tTyPVR0ERsf14oCUsjjS5qgeq3dGidRqEw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-types/listbox': 3.5.1(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/live-announcer@3.3.1: resolution: {integrity: sha512-hsc77U7S16trM86d+peqJCOCQ7/smO1cybgdpOuzXyiwcHQw8RQ4GrXrS37P4Ux/44E9nMZkOwATQRT2aK8+Ew==} dependencies: '@swc/helpers': 0.5.2 dev: false + /@react-aria/live-announcer@3.3.4: + resolution: {integrity: sha512-w8lxs35QrRrn6pBNzVfyGOeqWdxeVKf9U6bXIVwhq7rrTqRULL8jqy8RJIMfIs1s8G5FpwWYjyBOjl2g5Cu1iA==} + dependencies: + '@swc/helpers': 0.5.5 + dev: false + /@react-aria/menu@3.12.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Nsujv3b61WR0gybDKnBjAeyxDVJOfPLMggRUf9SQDfPWnrPXEsAFxaPaVcAkzlfI4HiQs1IxNwsKFNpc3PPZTQ==} peerDependencies: @@ -10048,6 +10426,41 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/menu@3.15.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-vvUmVjJwIg3h2r+7isQXTwlmoDlPAFBckHkg94p3afrT1kNOTHveTsaVl17mStx/ymIioaAi3PrIXk/PZXp1jw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/overlays': 3.23.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/menu': 3.8.2(react@18.2.0) + '@react-stately/tree': 3.8.4(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/menu': 3.9.11(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@react-aria/meter@3.4.16(react@18.2.0): + resolution: {integrity: sha512-hJqKnEE6mmK2Psx5kcI7NZ44OfTg0Bp7DatQSQ4zZE4yhnykRRwxqSKjze37tPR63cCqgRXtQ5LISfBfG54c0Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/progress': 3.4.16(react@18.2.0) + '@react-types/meter': 3.4.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/meter@3.4.9(react@18.2.0): resolution: {integrity: sha512-1/FHFmFmSyfQBJ2oH152lp4nps76v1UdhnFbIsmRIH+0g0IfMv1yDT2M9dIZ/b9DgVZSx527FmWOXm0eHGKD6w==} peerDependencies: @@ -10081,6 +10494,27 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/numberfield@3.11.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nvEWiQcWRwj6O2JXmkXEeWoBX/GVZT9zumFJcew3XknGTWJUr3h2AOymIQFt9g4mpag8IgOFEpSIlwhtZHdp1A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/spinbutton': 3.6.8(react-dom@18.2.0)(react@18.2.0) + '@react-aria/textfield': 3.14.8(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/numberfield': 3.9.6(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/numberfield': 3.8.5(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/overlays@3.20.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2m7MpRJL5UucbEuu08lMHsiFJoDowkJV4JAIFBZYK1NzVH0vF/A+w9HRNM7jRwx2DUxE+iIsZnl8yKV/7KY8OQ==} peerDependencies: @@ -10102,6 +10536,41 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/overlays@3.23.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-vjlplr953YAuJfHiP4O+CyrTlr6OaFgXAGrzWq4MVMjnpV/PT5VRJWYFHR0sUGlHTPqeKS4NZbi/xCSgl/3pGQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/ssr': 3.9.5(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-aria/visually-hidden': 3.8.15(react@18.2.0) + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/overlays': 3.8.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@react-aria/progress@3.4.16(react@18.2.0): + resolution: {integrity: sha512-RbDIFQg4+/LG+KYZeLAijt2zH7K2Gp0CY9RKWdho3nU5l3/w57Fa7NrfDGWtpImrt7bR2nRmXMA6ESfr7THfrg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/progress': 3.5.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/progress@3.4.9(react@18.2.0): resolution: {integrity: sha512-CME1ZLsJHOmSgK8IAPOC/+vYO5Oc614mkEw5MluT/yclw5rMyjAkK1XsHLjEXy81uwPeiRyoQQIMPKG2/sMxFQ==} peerDependencies: @@ -10134,6 +10603,24 @@ packages: react: 18.2.0 dev: false + /@react-aria/radio@3.10.7(react@18.2.0): + resolution: {integrity: sha512-o2tqIe7xd1y4HeCBQfz/sXIwLJuI6LQbVoCQ1hgk/5dGhQ0LiuXohRYitGRl9zvxW8jYdgLULmOEDt24IflE8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/form': 3.0.8(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/radio': 3.10.7(react@18.2.0) + '@react-types/radio': 3.8.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/searchfield@3.7.1(react@18.2.0): resolution: {integrity: sha512-ebhnV/reNByIZzpcQLHIo1RQ+BrYS8HdwX624i9R7dep1gxGHXYEaqL9aSY+RdngNerB4OeiWmB75em9beSpjQ==} peerDependencies: @@ -10150,6 +10637,22 @@ packages: react: 18.2.0 dev: false + /@react-aria/searchfield@3.7.8(react@18.2.0): + resolution: {integrity: sha512-SsF5xwH8Us548QgzivvbM7nhFbw7pu23xnRRIuhlP3MwOR3jRUFh17NKxf3Z0jvrDv/u0xfm3JKHIgaUN0KJ2A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/textfield': 3.14.8(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/searchfield': 3.5.6(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/searchfield': 3.5.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/select@3.14.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pAy/+Xbj11Lx6bi/O1hWH0NSIDRxFb6V7N0ry2L8x7MALljh516VbpnAc5RgvbjbuKq0cHUAcdINOzOzpYWm4A==} peerDependencies: @@ -10174,6 +10677,30 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/select@3.14.9(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-tiNgMyA2G9nKnFn3pB/lMSgidNToxSFU7r6l4OcG+Vyr63J7B/3dF2lTXq8IYhlfOR3K3uQkjroSx52CmC3NDw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/form': 3.0.8(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/listbox': 3.13.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/menu': 3.15.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-aria/visually-hidden': 3.8.15(react@18.2.0) + '@react-stately/select': 3.6.7(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/select': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/selection@3.17.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xl2sgeGH61ngQeE05WOWWPVpGRTPMjQEFmsAWEprArFi4Z7ihSZgpGX22l1w7uSmtXM/eN/v0W8hUYUju5iXlQ==} peerDependencies: @@ -10191,6 +10718,23 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/selection@3.19.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GYoObXCXlmGK08hp7Qfl6Bk0U+bKP5YDWSsX+MzNjJsqzQSLm4S06tRB9ACM7gIo9dDCvL4IRxdSYTJAlJc6bw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/separator@3.3.9(react@18.2.0): resolution: {integrity: sha512-1wEXiaSJjq2+DR5TC0RKnUBsfZN+YXTzyI7XMzjQoc3YlclumX8wQtzPAOGOEjHB1JKUgo1Gw70FtupVXz58QQ==} peerDependencies: @@ -10202,6 +10746,34 @@ packages: react: 18.2.0 dev: false + /@react-aria/separator@3.4.2(react@18.2.0): + resolution: {integrity: sha512-Xql9Kg3VlGesEUC7QheE+L5b3KgBv0yxiUU+/4JP8V2vfU/XSz4xmprHEeq7KVQVOetn38iiXU8gA5g26SEsUA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-aria/slider@3.7.11(react@18.2.0): + resolution: {integrity: sha512-2WAwjANXPsA2LHJ5nxxV4c7ihFAzz2spaBz8+FJ7MDYE7WroYnE8uAXElea1aGo+Lk0DTiAdepLpBkggqPNanw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/slider': 3.5.7(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/slider': 3.7.5(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/slider@3.7.4(react@18.2.0): resolution: {integrity: sha512-OFJWeGSL2duVDFs/kcjlWsY6bqCVKZgM0aFn2QN4wmID+vfBvBnqGHAgWv3BCePTAPS3+GBjMN002TrftorjwQ==} peerDependencies: @@ -10235,6 +10807,22 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/spinbutton@3.6.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OJMAYRIZ0WrWE+5tZsywrSg4t+aOwl6vl/e1+J64YcGMM+p+AKd61KGG5T0OgNSORXjoVIZOmj6wZ6Od4xfPMw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/ssr@3.9.1(react@18.2.0): resolution: {integrity: sha512-NqzkLFP8ZVI4GSorS0AYljC13QW2sc8bDqJOkBvkAt3M8gbcAXJWVRGtZBCRscki9RZF+rNlnPdg0G0jYkhJcg==} engines: {node: '>= 12'} @@ -10245,6 +10833,16 @@ packages: react: 18.2.0 dev: false + /@react-aria/ssr@3.9.5(react@18.2.0): + resolution: {integrity: sha512-xEwGKoysu+oXulibNUSkXf8itW0npHHTa6c4AyYeZIJyRoegeteYuFpZUBPtIDE8RfHdNsSmE1ssOkxRnwbkuQ==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/switch@3.6.0(react@18.2.0): resolution: {integrity: sha512-YNWc5fGLNXE4XlmDAKyqAdllRiClGR7ki4KGFY7nL+xR5jxzjCGU3S3ToMK5Op3QSMGZLxY/aYmC4O+MvcoADQ==} peerDependencies: @@ -10257,6 +10855,19 @@ packages: react: 18.2.0 dev: false + /@react-aria/switch@3.6.7(react@18.2.0): + resolution: {integrity: sha512-yBNvKylhc3ZRQ0+7mD0mIenRRe+1yb8YaqMMZr8r3Bf87LaiFtQyhRFziq6ZitcwTJz5LEWjBihxbSVvUrf49w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/toggle': 3.10.7(react@18.2.0) + '@react-stately/toggle': 3.7.7(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/switch': 3.5.5(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/table@3.13.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-AzmETpyxwNqISTzwHJPs85x9gujG40IIsSOBUdp49oKhB85RbPLvMwhadp4wCVAoHw3erOC/TJxHtVc7o2K1LA==} peerDependencies: @@ -10283,6 +10894,31 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/table@3.15.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nQCLjlEvyJHyuijHw8ESqnA9fxNJfQHx0WPcl08VDEb8VxcE/MVzSAIedSWaqjG5k9Oflz6o/F/zHtzw4AFAow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/grid': 3.10.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/live-announcer': 3.3.4 + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-aria/visually-hidden': 3.8.15(react@18.2.0) + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/flags': 3.0.3 + '@react-stately/table': 3.12.2(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@react-types/grid': 3.2.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/table': 3.10.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/tabs@3.8.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Plw0K/5Qv35vYq7pHZFfQB2BF5OClFx4Abzo9hLVx4oMy3qb7i5lxmLBVbt81yPX/MdjYeP4zO1EHGBl4zMRhA==} peerDependencies: @@ -10301,6 +10937,24 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/tabs@3.9.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-aQZGAoOIg1B16qlvXIy6+rHbNBNVcWkGjOjeyvqTTPMjXt/FmElkICnqckI7MRJ1lTqzyppCOBitYOHSXRo8Uw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/tabs': 3.6.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/tabs': 3.3.9(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/tag@3.3.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-w7d8sVZqxTo8VFfeg2ixLp5kawtrcguGznVY4mt5aE6K8LMJOeNVDqNNfolfyia80VjOWjeX+RpVdVJRdrv/GQ==} peerDependencies: @@ -10321,6 +10975,26 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@react-aria/tag@3.4.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-iyJuATQ8t2cdLC7hiZm143eeZze/MtgxaMq0OewlI9TUje54bkw2Q+CjERdgisIo3Eemf55JJgylGrTcalEJAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/gridlist': 3.9.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-types/button': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@react-aria/textfield@3.14.1(react@18.2.0): resolution: {integrity: sha512-UMepuYtDdCgrUF4dMphNxrUm23xOmR54aZD1pbp9cJyfioVkJN35BTXZVkD0D07gHLn4RhxKIZxBortQQrLB9g==} peerDependencies: @@ -10338,6 +11012,23 @@ packages: react: 18.2.0 dev: false + /@react-aria/textfield@3.14.8(react@18.2.0): + resolution: {integrity: sha512-FHEvsHdE1cMR2B7rlf+HIneITrC40r201oLYbHAp3q26jH/HUujzFBB9I20qhXjyBohMWfQLqJhSwhs1VW1RJQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/form': 3.0.8(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/textfield': 3.9.6(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/toggle@3.10.0(react@18.2.0): resolution: {integrity: sha512-6cUf4V9TuG2J7AvXUdU/GspEPFCubUOID3mrselSe563RViy+mMZk0vUEOdyoNanDcEXl58W4dE3SGWxFn71vg==} peerDependencies: @@ -10352,6 +11043,21 @@ packages: react: 18.2.0 dev: false + /@react-aria/toggle@3.10.7(react@18.2.0): + resolution: {integrity: sha512-/RJQU8QlPZXRElZ3Tt10F5K5STgUBUGPpfuFUGuwF3Kw3GpPxYsA1YAVjxXz2MMGwS0+y6+U/J1xIs1AF0Jwzg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/toggle': 3.7.7(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/tooltip@3.7.0(react@18.2.0): resolution: {integrity: sha512-+u9Sftkfe09IDyPEnbbreFKS50vh9X/WTa7n1u2y3PenI9VreLpUR6czyzda4BlvQ95e9jQz1cVxUjxTNaZmBw==} peerDependencies: @@ -10367,6 +11073,21 @@ packages: react: 18.2.0 dev: false + /@react-aria/tooltip@3.7.7(react@18.2.0): + resolution: {integrity: sha512-UOTTDbbUz7OaE48VjNSWl+XQbYCUs5Gss4I3Tv1pfRLXzVtGYXv3ur/vRayvZR0xd12ANY26fZPNkSmCFpmiXw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-stately/tooltip': 3.4.12(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/tooltip': 3.4.11(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/utils@3.23.0(react@18.2.0): resolution: {integrity: sha512-fJA63/VU4iQNT8WUvrmll3kvToqMurD69CcgVmbQ56V7ZbvlzFi44E7BpnoaofScYLLtFWRjVdaHsohT6O/big==} peerDependencies: @@ -10380,6 +11101,31 @@ packages: react: 18.2.0 dev: false + /@react-aria/utils@3.25.2(react@18.2.0): + resolution: {integrity: sha512-GdIvG8GBJJZygB4L2QJP1Gabyn2mjFsha73I2wSe+o4DYeGWoJiMZRM06PyTIxLH4S7Sn7eVDtsSBfkc2VY/NA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/ssr': 3.9.5(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + clsx: 2.1.0 + react: 18.2.0 + dev: false + + /@react-aria/visually-hidden@3.8.15(react@18.2.0): + resolution: {integrity: sha512-l+sJ7xTdD5Sd6+rDNDaeJCSPnHOsI+BaJyApvb/YcVgHa7rB47lp6TXCWUCDItcPY4JqRGyeByRJVrtzBFTWCw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-aria/visually-hidden@3.8.8(react@18.2.0): resolution: {integrity: sha512-Cn2PYKD4ijGDtF0+dvsh8qa4y7KTNAlkTG6h20r8Q+6UTyRNmtE2/26QEaApRF8CBiNy9/BZC/ZC4FK2OjvCoA==} peerDependencies: @@ -11038,6 +11784,10 @@ packages: react: 18.2.0 dev: false + /@react-spring/rafz@9.7.4: + resolution: {integrity: sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA==} + dev: false + /@react-stately/calendar@3.4.3(react@18.2.0): resolution: {integrity: sha512-OrEcdskszDjnjVnFuSiDC2PVBJ6lWMCJROD5s6W1LUehUtBp8LX9wPavAGHV43LbhN9ldj560sxaQ4WCddrRCA==} peerDependencies: @@ -11051,6 +11801,19 @@ packages: react: 18.2.0 dev: false + /@react-stately/calendar@3.5.4(react@18.2.0): + resolution: {integrity: sha512-R2011mtFSXIjzMXaA+CZ1sflPm9XkTBMqVk77Bnxso2ZsG7FUX8nqFmaDavxwTuHFC6OUexAGSMs8bP9KycTNg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/calendar': 3.4.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/checkbox@3.6.1(react@18.2.0): resolution: {integrity: sha512-rOjFeVBy32edYwhKiHj3ZLdLeO+xZ2fnBwxnOBjcygnw4Neygm8FJH/dB1J0hdYYR349yby86ED2x0wRc84zPw==} peerDependencies: @@ -11064,6 +11827,19 @@ packages: react: 18.2.0 dev: false + /@react-stately/checkbox@3.6.8(react@18.2.0): + resolution: {integrity: sha512-c8TWjU67XHHBCpqj6+FXXhQUWGr2Pil1IKggX81pkedhWiJl3/7+WHJuZI0ivGnRjp3aISNOG8UNVlBEjS9E8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/collections@3.10.4(react@18.2.0): resolution: {integrity: sha512-OHhCrItGt4zB2bSrgObRo0H2SC7QlkH8ReGxo+NVIWchXRLRoiWBP7S+IwleewEo5gOqDVPY3hqA9n4iiI8twg==} peerDependencies: @@ -11074,6 +11850,16 @@ packages: react: 18.2.0 dev: false + /@react-stately/collections@3.10.9(react@18.2.0): + resolution: {integrity: sha512-plyrng6hOQMG8LrjArMA6ts/DgWyXln3g90/hFNbqe/hdVYF53sDVsj8Jb+5LtoYTpiAlV6eOvy1XR0vPZUf8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/combobox@3.8.1(react@18.2.0): resolution: {integrity: sha512-FaWkqTXQdWg7ptaeU4iPcqF/kxbRg2ZNUcvW/hiL/enciV5tRCsddvfNqvDvy1L30z9AUwlp9MWqzm/DhBITCw==} peerDependencies: @@ -11091,6 +11877,23 @@ packages: react: 18.2.0 dev: false + /@react-stately/combobox@3.9.2(react@18.2.0): + resolution: {integrity: sha512-ZsbAcD58IvxZqwYxg9d2gOf8R/k5RUB2TPUiGKD6wgWfEKH6SDzY3bgRByHGOyMCyJB62cHjih/ZShizNTguqA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-stately/select': 3.6.7(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/combobox': 3.12.1(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/data@3.11.0(react@18.2.0): resolution: {integrity: sha512-0BlPT58WrAtUvpiEfUuyvIsGFTzp/9vA5y+pk53kGJhOdc5tqBGHi9cg40pYE/i1vdHJGMpyHGRD9nkQb8wN3Q==} peerDependencies: @@ -11101,6 +11904,22 @@ packages: react: 18.2.0 dev: false + /@react-stately/datepicker@3.10.2(react@18.2.0): + resolution: {integrity: sha512-pa5IZUw+49AyOnddwu4XwU2kI5eo/1thbiIVNHP8uDpbbBrBkquSk3zVFDAGX1cu/I1U2VUkt64U/dxgkwaMQw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@internationalized/string': 3.2.3 + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/datepicker': 3.8.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/datepicker@3.9.1(react@18.2.0): resolution: {integrity: sha512-o5xLvlZGJyAbTev2yruGlV2fzQyIDuYTgL19TTt0W0WCfjGGr/AAA9GjGXXmyoRA7sZMxqIPnnv7lNrdA38ofA==} peerDependencies: @@ -11128,12 +11947,29 @@ packages: react: 18.2.0 dev: false + /@react-stately/dnd@3.4.2(react@18.2.0): + resolution: {integrity: sha512-VrHmNoNdVGrx5JHdz/zewmN+N8rlZe+vL/iAOLmvQ74RRLEz8KDFnHdlhgKg1AZqaSg3JJ18BlHEkS7oL1n+tA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/flags@3.0.0: resolution: {integrity: sha512-e3i2ItHbIa0eEwmSXAnPdD7K8syW76JjGe8ENxwFJPW/H1Pu9RJfjkCb/Mq0WSPN/TpxBb54+I9TgrGhbCoZ9w==} dependencies: '@swc/helpers': 0.4.14 dev: false + /@react-stately/flags@3.0.3: + resolution: {integrity: sha512-/ha7XFA0RZTQsbzSPwu3KkbNMgbvuM0GuMTYLTBWpgBrovBNTM+QqI/PfZTdHg8PwCYF4H5Y8gjdSpdulCvJFw==} + dependencies: + '@swc/helpers': 0.5.5 + dev: false + /@react-stately/form@3.0.0(react@18.2.0): resolution: {integrity: sha512-C8wkfFmtx1escizibhdka5JvTy9/Vp173CS9cakjvWTmnjYYC1nOlzwp7BsYWTgerCFbRY/BU/Cf/bJDxPiUKQ==} peerDependencies: @@ -11144,6 +11980,16 @@ packages: react: 18.2.0 dev: false + /@react-stately/form@3.0.5(react@18.2.0): + resolution: {integrity: sha512-J3plwJ63HQz109OdmaTqTA8Qhvl3gcYYK7DtgKyNP6mc/Me2Q4tl2avkWoA+22NRuv5m+J8TpBk4AVHUEOwqeQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/grid@3.8.4(react@18.2.0): resolution: {integrity: sha512-rwqV1K4lVhaiaqJkt4TfYqdJoVIyqvSm98rKAYfCNzrKcivVpoiCMJ2EMt6WlYCjDVBdEOQ7fMV1I60IV0pntA==} peerDependencies: @@ -11157,6 +12003,19 @@ packages: react: 18.2.0 dev: false + /@react-stately/grid@3.9.2(react@18.2.0): + resolution: {integrity: sha512-2gK//sqAqg2Xaq6UITTFQwFUJnBRgcW+cKBVbFt+F8d152xB6UwwTS/K79E5PUkOotwqZgTEpkrSFs/aVxCLpw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-types/grid': 3.2.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/list@3.10.2(react@18.2.0): resolution: {integrity: sha512-INt+zofkIg2KN8B95xPi9pJG7ZFWAm30oIm/lCPBqM3K1Nm03/QaAbiQj2QeJcOsG3lb7oqI6D6iwTolwJkjIQ==} peerDependencies: @@ -11170,6 +12029,19 @@ packages: react: 18.2.0 dev: false + /@react-stately/list@3.10.8(react@18.2.0): + resolution: {integrity: sha512-rHCiPLXd+Ry3ztR9DkLA5FPQeH4Zd4/oJAEDWJ77W3oBBOdiMp3ZdHDLP7KBRh17XGNLO/QruYoHWAQTPiMF4g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/menu@3.6.0(react@18.2.0): resolution: {integrity: sha512-OB6CjNyfOkAuirqx1oTL8z8epS9WDzLyrXjmRnxdiCU9EgRXLGAQNECuO7VIpl58oDry8tgRJiJ8fn8FivWSQA==} peerDependencies: @@ -11182,6 +12054,18 @@ packages: react: 18.2.0 dev: false + /@react-stately/menu@3.8.2(react@18.2.0): + resolution: {integrity: sha512-lt6hIHmSixMzkKx1rKJf3lbAf01EmEvvIlENL20GLiU9cRbpPnPJ1aJMZ5Ad5ygglA7wAemAx+daPhlTQfF2rg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-types/menu': 3.9.11(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/numberfield@3.8.0(react@18.2.0): resolution: {integrity: sha512-1XvB8tDOvZKcFnMM6qNLEaTVJcIc0jRFS/9jtS8MzalZvh8DbKi0Ucm1bGU7S5rkCx2QWqZ0rGOIm2h/RlcpkA==} peerDependencies: @@ -11195,6 +12079,30 @@ packages: react: 18.2.0 dev: false + /@react-stately/numberfield@3.9.6(react@18.2.0): + resolution: {integrity: sha512-p2R9admGLI439qZzB39dyANhkruprJJtZwuoGVtxW/VD0ficw6BrPVqAaKG25iwKPkmveleh9p8o+yRqjGedcQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/number': 3.5.3 + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/numberfield': 3.8.5(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-stately/overlays@3.6.10(react@18.2.0): + resolution: {integrity: sha512-XxZ2qScT5JPwGk9qiVJE4dtVh3AXTcYwGRA5RsHzC26oyVVsegPqY2PmNJGblAh6Q57VyodoVUyebE0Eo5CzRw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/overlays': 3.8.9(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/overlays@3.6.4(react@18.2.0): resolution: {integrity: sha512-tHEaoAGpE9dSnsskqLPVKum59yGteoSqsniTopodM+miQozbpPlSjdiQnzGLroy5Afx5OZYClE616muNHUILXA==} peerDependencies: @@ -11219,6 +12127,19 @@ packages: react: 18.2.0 dev: false + /@react-stately/radio@3.10.7(react@18.2.0): + resolution: {integrity: sha512-ZwGzFR+sGd42DxRlDTp3G2vLZyhMVtgHkwv2BxazPHxPMvLO9yYl7+3PPNxAmhMB4tg2u9CrzffpGX2rmEJEXA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/radio': 3.8.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/searchfield@3.5.0(react@18.2.0): resolution: {integrity: sha512-SStjChkn/33pEn40slKQPnBnmQYyxVazVwPjiBkdeVejC42lUVairUTrGJgF0PNoZTbxn0so2/XzjqTC9T8iCw==} peerDependencies: @@ -11230,6 +12151,17 @@ packages: react: 18.2.0 dev: false + /@react-stately/searchfield@3.5.6(react@18.2.0): + resolution: {integrity: sha512-gVzU0FeWiLYD8VOYRgWlk79Qn7b2eirqOnWhtI5VNuGN8WyNaCIuBp6SkXTW2dY8hs2Hzn8HlMbgy1MIc7130Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/searchfield': 3.5.8(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/select@3.6.1(react@18.2.0): resolution: {integrity: sha512-e5ixtLiYLlFWM8z1msDqXWhflF9esIRfroptZsltMn1lt2iImUlDRlOTZlMtPQzUrDWoiHXRX88sSKUM/jXjQQ==} peerDependencies: @@ -11244,6 +12176,20 @@ packages: react: 18.2.0 dev: false + /@react-stately/select@3.6.7(react@18.2.0): + resolution: {integrity: sha512-hCUIddw0mPxVy1OH6jhyaDwgNea9wESjf+MYdnnTG/abRB+OZv/dWScd87OjzVsHTHWcw7CN4ZzlJoXm0FJbKQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/form': 3.0.5(react@18.2.0) + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-types/select': 3.9.6(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/selection@3.14.2(react@18.2.0): resolution: {integrity: sha512-mL7OoiUgVWaaF7ks5XSxgbXeShijYmD4G3bkBHhqkpugU600QH6BM2hloCq8KOUupk1y8oTljPtF9EmCv375DA==} peerDependencies: @@ -11256,6 +12202,18 @@ packages: react: 18.2.0 dev: false + /@react-stately/selection@3.16.2(react@18.2.0): + resolution: {integrity: sha512-C4eSKw7BIZHJLPzwqGqCnsyFHiUIEyryVQZTJDt6d0wYBOHU6k1pW+Q4VhrZuzSv+IMiI2RkiXeJKc55f0ZXrg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/slider@3.5.0(react@18.2.0): resolution: {integrity: sha512-dOVpIxb7XKuiRxgpHt1bUSlsklciFki100tKIyBPR+Okar9iC/CwLYROYgVfLkGe77jEBNkor9tDLjDGEWcc1w==} peerDependencies: @@ -11268,6 +12226,18 @@ packages: react: 18.2.0 dev: false + /@react-stately/slider@3.5.7(react@18.2.0): + resolution: {integrity: sha512-gEIGTcpBLcXixd8LYiLc8HKrBiGQJltrrEGoOvvTP8KVItXQxmeL+JiSsh8qgOoUdRRpzmAoFNUKGEg2/gtN8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/slider': 3.7.5(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/table@3.11.4(react@18.2.0): resolution: {integrity: sha512-dWINJIEOKQl4qq3moq+S8xCD3m+yJqBj0dahr+rOkS+t2uqORwzsusTM35D2T/ZHZi49S2GpE7QuDa+edCynPw==} peerDependencies: @@ -11285,6 +12255,23 @@ packages: react: 18.2.0 dev: false + /@react-stately/table@3.12.2(react@18.2.0): + resolution: {integrity: sha512-dUcsrdALylhWz6exqIoqtR/dnrzjIAptMyAUPT378Y/mCYs4PxKkHSvtPEQrZhdQS1ALIIgfeg9KUVIempoXPw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/flags': 3.0.3 + '@react-stately/grid': 3.9.2(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/grid': 3.2.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/table': 3.10.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/tabs@3.6.3(react@18.2.0): resolution: {integrity: sha512-Nj+Gacwa2SIzYIvHW40GsyX4Q6c8kF7GOuXESeQswbCjnwqhrSbDBp+ngPcUPUJxqFh6JhDCVwAS3wMhUoyUwA==} peerDependencies: @@ -11297,6 +12284,18 @@ packages: react: 18.2.0 dev: false + /@react-stately/tabs@3.6.9(react@18.2.0): + resolution: {integrity: sha512-YZDqZng3HrRX+uXmg6u78x73Oi24G5ICpiXVqDKKDkO333XCA5H8MWItiuPZkYB2h3SbaCaLqSobLkvCoWYpNQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/list': 3.10.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/tabs': 3.3.9(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/toggle@3.7.0(react@18.2.0): resolution: {integrity: sha512-TRksHkCJk/Xogq4181g3CYgJf+EfsJCqX5UZDSw1Z1Kgpvonjmdf6FAfQfCh9QR2OuXUL6hOLUDVLte5OPI+5g==} peerDependencies: @@ -11308,6 +12307,28 @@ packages: react: 18.2.0 dev: false + /@react-stately/toggle@3.7.7(react@18.2.0): + resolution: {integrity: sha512-AS+xB4+hHWa3wzYkbS6pwBkovPfIE02B9SnuYTe0stKcuejpWKo5L3QMptW0ftFYsW3ZPCXuneImfObEw2T01A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/checkbox': 3.8.3(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-stately/tooltip@3.4.12(react@18.2.0): + resolution: {integrity: sha512-QKYT/cze7n9qaBsk7o5ais3jRfhYCzcVRfps+iys/W+/9FFbbhjfQG995Lwi6b+vGOHWfXxXpwmyIO2tzM1Iog==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/overlays': 3.6.10(react@18.2.0) + '@react-types/tooltip': 3.4.11(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/tooltip@3.4.6(react@18.2.0): resolution: {integrity: sha512-uL93bmsXf+OOgpKLPEKfpDH4z+MK2CuqlqVxx7rshN0vjWOSoezE5nzwgee90+RpDrLNNNWTNa7n+NkDRpI1jA==} peerDependencies: @@ -11332,6 +12353,28 @@ packages: react: 18.2.0 dev: false + /@react-stately/tree@3.8.4(react@18.2.0): + resolution: {integrity: sha512-HFNclIXJ/3QdGQWxXbj+tdlmIX/XwCfzAMB5m26xpJ6HtJhia6dtx3GLfcdyHNjmuRbAsTBsAAnnVKBmNRUdIQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-stately/collections': 3.10.9(react@18.2.0) + '@react-stately/selection': 3.16.2(react@18.2.0) + '@react-stately/utils': 3.10.3(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + + /@react-stately/utils@3.10.3(react@18.2.0): + resolution: {integrity: sha512-moClv7MlVSHpbYtQIkm0Cx+on8Pgt1XqtPx6fy9rQFb2DNc9u1G3AUVnqA17buOkH1vLxAtX4MedlxMWyRCYYA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@swc/helpers': 0.5.5 + react: 18.2.0 + dev: false + /@react-stately/utils@3.9.0(react@18.2.0): resolution: {integrity: sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw==} peerDependencies: @@ -11362,6 +12405,16 @@ packages: react: 18.2.0 dev: false + /@react-types/breadcrumbs@3.7.7(react@18.2.0): + resolution: {integrity: sha512-ZmhXwD2LLzfEA2OvOCp/QvXu8A/Edsrn5q0qUDGsmOZj9SCVeT82bIv8P+mQnATM13mi2gyoik6102Jc1OscJA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/link': 3.5.7(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/button@3.9.1(react@18.2.0): resolution: {integrity: sha512-bf9iTar3PtqnyV9rA+wyFyrskZKhwmOuOd/ifYIjPs56YNVXWH5Wfqj6Dx3xdFBgtKx8mEVQxVhoX+WkHX+rtw==} peerDependencies: @@ -11371,6 +12424,15 @@ packages: react: 18.2.0 dev: false + /@react-types/button@3.9.6(react@18.2.0): + resolution: {integrity: sha512-8lA+D5JLbNyQikf8M/cPP2cji91aVTcqjrGpDqI7sQnaLFikM8eFR6l1ZWGtZS5MCcbfooko77ha35SYplSQvw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/calendar@3.4.3(react@18.2.0): resolution: {integrity: sha512-96x57ctX5wNEl+8et3sc2NQm8neOJayEeqOQQpyPtI7jyvst/xBrKCwysf9W/dhgPlUC+KeBAYFWfjd5hFVHYA==} peerDependencies: @@ -11381,6 +12443,16 @@ packages: react: 18.2.0 dev: false + /@react-types/calendar@3.4.9(react@18.2.0): + resolution: {integrity: sha512-O/PS9c21HgO9qzxOyZ7/dTccxabFZdF6tj3UED4DrBw7AN3KZ7JMzwzYbwHinOcO7nUcklGgNoAIHk45UAKR9g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/checkbox@3.6.0(react@18.2.0): resolution: {integrity: sha512-vgbuJzQpVCNT5AZWV0OozXCnihqrXxoZKfJFIw0xro47pT2sn3t5UC4RA9wfjDGMoK4frw1K/4HQLsQIOsPBkw==} peerDependencies: @@ -11390,6 +12462,15 @@ packages: react: 18.2.0 dev: false + /@react-types/checkbox@3.8.3(react@18.2.0): + resolution: {integrity: sha512-f4c1mnLEt0iS1NMkyZXgT3q3AgcxzDk7w6MSONOKydcnh0xG5L2oefY14DhVDLkAuQS7jThlUFwiAs+MxiO3MA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/combobox@3.10.0(react@18.2.0): resolution: {integrity: sha512-1IXSNS02TPbguyYopaW2snU6sZusbClHrEyVr4zPeexTV4kpUUBNXOzFQ+eSQRR0r2XW57Z0yRW4GJ6FGU0yCA==} peerDependencies: @@ -11399,6 +12480,15 @@ packages: react: 18.2.0 dev: false + /@react-types/combobox@3.12.1(react@18.2.0): + resolution: {integrity: sha512-bd5YwHZWtgnJx4jGbplWbYzXj7IbO5w3IY5suNR7r891rx6IktquZ8GQwyYH0pQ/x+X5LdK2xI59i6+QC2PmlA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/datepicker@3.7.1(react@18.2.0): resolution: {integrity: sha512-5juVDULOytNzkotqX8j5mYKJckeIpkgbHqVSGkPgLw0++FceIaSZ6RH56cqLup0pO45paqIt9zHh+QXBYX+syg==} peerDependencies: @@ -11411,6 +12501,28 @@ packages: react: 18.2.0 dev: false + /@react-types/datepicker@3.8.2(react@18.2.0): + resolution: {integrity: sha512-Ih4F0bNVGrEuwCD8XmmBAspuuOBsj/Svn/pDFtC2RyAZjXfWh+sI+n4XLz/sYKjvARh5TUI8GNy9smYS4vYXug==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/date': 3.5.5 + '@react-types/calendar': 3.4.9(react@18.2.0) + '@react-types/overlays': 3.8.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-types/dialog@3.5.12(react@18.2.0): + resolution: {integrity: sha512-JmpQbSpXltqEyYfEwoqDolABIiojeExkqolHNdQlayIsfFuSxZxNwXZPOpz58Ri/iwv21JP7K3QF0Gb2Ohxl9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/overlays': 3.8.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/dialog@3.5.7(react@18.2.0): resolution: {integrity: sha512-geYoqAyQaTLG43AaXdMUVqZXYgkSifrD9cF7lR2kPAT0uGFv0YREi6ieU+aui8XJ83EW0xcxP+EPWd2YkN4D4w==} peerDependencies: @@ -11430,6 +12542,15 @@ packages: react: 18.2.0 dev: false + /@react-types/grid@3.2.8(react@18.2.0): + resolution: {integrity: sha512-6PJrpukwMqlv3IhJSDkJuVbhHM8Oe6hd2supWqd9adMXrlSP7QHt9a8SgFcFblCCTx8JzUaA0PvY5sTudcEtOQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/link@3.5.2(react@18.2.0): resolution: {integrity: sha512-/s51/WejmpLiyxOgP89s4txgxYoGaPe8pVDItVo1h4+BhU1Puyvgv/Jx8t9dPvo6LUXbraaN+SgKk/QDxaiirw==} peerDependencies: @@ -11439,6 +12560,15 @@ packages: react: 18.2.0 dev: false + /@react-types/link@3.5.7(react@18.2.0): + resolution: {integrity: sha512-2WyaVmm1qr9UrSG3Dq6iz+2ziuVp+DH8CsYZ9CA6aNNb6U18Hxju3LTPb4a5gM0eC7W0mQGNBmrgGlAdDZEJOw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/listbox@3.4.6(react@18.2.0): resolution: {integrity: sha512-XOQvrTqNh5WIPDvKiWiep8T07RAsMfjAXTjDbnjxVlKACUXkcwpts9kFaLnJ9LJRFt6DwItfP+WMkzvmx63/NQ==} peerDependencies: @@ -11448,6 +12578,25 @@ packages: react: 18.2.0 dev: false + /@react-types/listbox@3.5.1(react@18.2.0): + resolution: {integrity: sha512-n5bOgD9lgfK1qaLtag9WPnu151SwXBCNn/OgGY/Br9mWRl+nPUEYtFcPX+2VCld7uThf54kwrTmzlFnaraIlcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-types/menu@3.9.11(react@18.2.0): + resolution: {integrity: sha512-IguQVF70d7aHXgWB1Rd2a/PiIuLZ2Nt7lyayJshLcy/NLOYmgpTmTyn2WCtlA5lTfQwmQrNFf4EvnWkeljJXdA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/overlays': 3.8.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/menu@3.9.6(react@18.2.0): resolution: {integrity: sha512-w/RbFInOf4nNayQDv5c2L8IMJbcFOkBhsT3xvvpTy+CHvJcQdjggwaV1sRiw7eF/PwB81k2CwigmidUzHJhKDg==} peerDependencies: @@ -11467,6 +12616,15 @@ packages: react: 18.2.0 dev: false + /@react-types/meter@3.4.3(react@18.2.0): + resolution: {integrity: sha512-Y2fX5CTAPGRKxVSeepbeyN6/K+wlF9pMRcNxTSU2qDwdoFqNCtTWMcWuCsU/Y2L/zU0jFWu4x0Vo7WkrcsgcMA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/progress': 3.5.6(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/numberfield@3.7.0(react@18.2.0): resolution: {integrity: sha512-gaGi+vqm1Y8LCWRsWYUjcGftPIzl+8W2VOfkgKMLM8y76nnwTPtmAqs+Ap1cg7sEJSfsiKMq93e9yvP3udrC2w==} peerDependencies: @@ -11476,6 +12634,15 @@ packages: react: 18.2.0 dev: false + /@react-types/numberfield@3.8.5(react@18.2.0): + resolution: {integrity: sha512-LVWggkxwd1nyVZomXBPfQA1E4I4/i4PBifjcDs2AfcV7q5RE9D+DVIDXsYucVOBxPlDOxiAq/T9ypobspWSwHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/overlays@3.8.4(react@18.2.0): resolution: {integrity: sha512-pfgNlQnbF6RB/R2oSxyqAP3Uzz0xE/k5q4n5gUeCDNLjY5qxFHGE8xniZZ503nZYw6VBa9XMN1efDOKQyeiO0w==} peerDependencies: @@ -11485,6 +12652,15 @@ packages: react: 18.2.0 dev: false + /@react-types/overlays@3.8.9(react@18.2.0): + resolution: {integrity: sha512-9ni9upQgXPnR+K9cWmbYWvm3ll9gH8P/XsEZprqIV5zNLMF334jADK48h4jafb1X9RFnj0WbHo6BqcSObzjTig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/progress@3.5.1(react@18.2.0): resolution: {integrity: sha512-CqsUjczUK/SfuFzDcajBBaXRTW0D3G9S/yqLDj9e8E0ii+lGDLt1PHj24t1J7E88U2rVYqmM9VL4NHTt8o3IYA==} peerDependencies: @@ -11494,6 +12670,15 @@ packages: react: 18.2.0 dev: false + /@react-types/progress@3.5.6(react@18.2.0): + resolution: {integrity: sha512-Nh43sjQ5adyN1bTHBPRaIPhXUdBqP0miYeJpeMY3V/KUl4qmouJLwDnccwFG4xLm6gBfYe22lgbbV7nAfNnuTQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/radio@3.7.0(react@18.2.0): resolution: {integrity: sha512-EcwGAXzSHjSqpFZha7xn3IUrhPiJLj+0yb1Ip0qPmhWz0VVw2DwrkY7q/jfaKroVvQhTo2TbfGhcsAQrt0fRqg==} peerDependencies: @@ -11503,6 +12688,15 @@ packages: react: 18.2.0 dev: false + /@react-types/radio@3.8.3(react@18.2.0): + resolution: {integrity: sha512-fUVJt4Bb6jOReFqnhHVNxWXH7t6c60uSFfoPKuXt/xI9LL1i2jhpur0ggpTfIn3qLIAmNBU6bKBCWAdr4KjeVQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/searchfield@3.5.2(react@18.2.0): resolution: {integrity: sha512-JAK2/Kg4Dr393FYfbRw0TlXKnJPX77sq1x/ZBxtO6p64+MuuIYKqw0i9PwDlo1PViw2QI5u8GFhKA2TgemY9uA==} peerDependencies: @@ -11513,6 +12707,16 @@ packages: react: 18.2.0 dev: false + /@react-types/searchfield@3.5.8(react@18.2.0): + resolution: {integrity: sha512-EcdqalHNIC6BJoRfmqUhAvXRd3aHkWlV1cFCz57JJKgUEFYyXPNrXd1b73TKLzTXEk+X/D6LKV15ILYpEaxu8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + '@react-types/textfield': 3.9.6(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/select@3.9.1(react@18.2.0): resolution: {integrity: sha512-EpKSxrnh8HdZvOF9dHQkjivAcdIp1K81FaxmvosH8Lygqh0iYXxAdZGtKLMyBoPI8YFhA+rotIzTcOqgCCnqWA==} peerDependencies: @@ -11522,6 +12726,15 @@ packages: react: 18.2.0 dev: false + /@react-types/select@3.9.6(react@18.2.0): + resolution: {integrity: sha512-cVSFR0eJLup/ht1Uto+y8uyLmHO89J6wNh65SIHb3jeVz9oLBAedP3YNI2qB+F9qFMUcA8PBSLXIIuT6gXzLgQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/shared@3.22.0(react@18.2.0): resolution: {integrity: sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA==} peerDependencies: @@ -11530,6 +12743,14 @@ packages: react: 18.2.0 dev: false + /@react-types/shared@3.24.1(react@18.2.0): + resolution: {integrity: sha512-AUQeGYEm/zDTN6zLzdXolDxz3Jk5dDL7f506F07U8tBwxNNI3WRdhU84G0/AaFikOZzDXhOZDr3MhQMzyE7Ydw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + react: 18.2.0 + dev: false + /@react-types/slider@3.7.0(react@18.2.0): resolution: {integrity: sha512-uyQXUVFfqc9SPUW0LZLMan2n232F/OflRafiHXz9viLFa9tVOupVa7GhASRAoHojwkjoJ1LjFlPih7g5dOZ0/Q==} peerDependencies: @@ -11539,6 +12760,15 @@ packages: react: 18.2.0 dev: false + /@react-types/slider@3.7.5(react@18.2.0): + resolution: {integrity: sha512-bRitwQRQjQoOcKEdPMljnvm474dwrmsc6pdsVQDh/qynzr+KO9IHuYc3qPW53WVE2hMQJDohlqtCAWQXWQ5Vcg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/switch@3.5.0(react@18.2.0): resolution: {integrity: sha512-/wNmUGjk69bP6t5k2QkAdrNN5Eb9Rz4dOyp0pCPmoeE+5haW6sV5NmtkvWX1NSc4DQz1xL/a5b+A0vxPCP22Jw==} peerDependencies: @@ -11548,6 +12778,25 @@ packages: react: 18.2.0 dev: false + /@react-types/switch@3.5.5(react@18.2.0): + resolution: {integrity: sha512-SZx1Bd+COhAOs/RTifbZG+uq/llwba7VAKx7XBeX4LeIz1dtguy5bigOBgFTMQi4qsIVCpybSWEEl+daj4XFPw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-types/table@3.10.1(react@18.2.0): + resolution: {integrity: sha512-xsNh0Gm4GtNeSknZqkMsfGvc94fycmfhspGO+FzQKim2hB5k4yILwd+lHYQ2UKW6New9GVH/zN2Pd3v67IeZ2g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/grid': 3.2.8(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/table@3.9.2(react@18.2.0): resolution: {integrity: sha512-brw5JUANOzBa2rYNpN8AIl9nDZ9RwRZC6G/wTM/JhtirjC1S42oCtf8Ap5rWJBdmMG/5KOfcGNcAl/huyqb3gg==} peerDependencies: @@ -11567,6 +12816,15 @@ packages: react: 18.2.0 dev: false + /@react-types/tabs@3.3.9(react@18.2.0): + resolution: {integrity: sha512-3Q9kRVvg/qDyeJR/W1+C2z2OyvDWQrSLvOCvAezX5UKzww4rBEAA8OqBlyDwn7q3fiwrh/m64l6p+dbln+RdxQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/textfield@3.9.0(react@18.2.0): resolution: {integrity: sha512-D/DiwzsfkwlAg3uv8hoIfwju+zhB/hWDEdTvxQbPkntDr0kmN/QfI17NMSzbOBCInC4ABX87ViXLGxr940ykGA==} peerDependencies: @@ -11576,6 +12834,25 @@ packages: react: 18.2.0 dev: false + /@react-types/textfield@3.9.6(react@18.2.0): + resolution: {integrity: sha512-0uPqjJh4lYp1aL1HL9IlV8Cgp8eT0PcsNfdoCktfkLytvvBPmox2Pfm57W/d0xTtzZu2CjxhYNTob+JtGAOeXA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-types/tooltip@3.4.11(react@18.2.0): + resolution: {integrity: sha512-WPikHQxeT5Lb09yJEaW6Ja3ecE0g1YM6ukWYS2v/iZLUPn5YlYrGytspuCYQNSh/u7suCz4zRLEHYCl7OCigjw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@react-types/overlays': 3.8.9(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + dev: false + /@react-types/tooltip@3.4.6(react@18.2.0): resolution: {integrity: sha512-RaZewdER7ZcsNL99RhVHs8kSLyzIBkwc0W6eFZrxST2MD9J5GzkVWRhIiqtFOd5U1aYnxdJ6woq72Ef+le6Vfw==} peerDependencies: @@ -11894,6 +13171,14 @@ packages: dev: true optional: true + /@rollup/rollup-darwin-arm64@4.21.3: + resolution: {integrity: sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@rollup/rollup-darwin-x64@4.13.2: resolution: {integrity: sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==} cpu: [x64] @@ -12937,13 +14222,26 @@ packages: '@babel/code-frame': 7.22.13 '@babel/runtime': 7.24.5 '@types/aria-query': 5.0.1 - aria-query: 5.1.3 + aria-query: 5.3.0 chalk: 4.1.2 dom-accessibility-api: 0.5.15 lz-string: 1.4.4 pretty-format: 27.5.1 dev: true + /@testing-library/jest-dom@6.5.0: + resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + dependencies: + '@adobe/css-tools': 4.4.0 + aria-query: 5.3.0 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + dev: false + /@tootallnate/quickjs-emscripten@0.23.0: resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} dev: false @@ -13132,7 +14430,6 @@ packages: /@types/cookie@0.6.0: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - dev: false /@types/cors@2.8.17: resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} @@ -13275,6 +14572,10 @@ packages: '@types/node': 18.19.20 dev: false + /@types/invariant@2.2.37: + resolution: {integrity: sha512-IwpIMieE55oGWiXkQPSBY1nw1nFs6bsKXTFskNY8sdS17K24vyEBRQZEwlRS7ZmXCWnJcQtbxWzly+cODWGs2A==} + dev: false + /@types/is-ci@3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: @@ -14236,10 +15537,39 @@ packages: tslib: 2.6.2 dev: false + /@window-splitter/state@0.4.1: + resolution: {integrity: sha512-JbsCYEWFXs79Gxudtv+XTYE2tdidPg8oGoIbUyh9QtdYUNUO/QKI2ORk2WFNIkk2ZzTWMXquI92s1nj4sAhZ+w==} + engines: {node: '>=18.0.0'} + dependencies: + '@react-spring/rafz': 9.7.4 + '@types/invariant': 2.2.37 + big.js: 6.2.2 + invariant: 2.2.4 + universal-cookie: 7.2.0 + xstate: 5.18.1 + dev: false + /@xobotyi/scrollbar-width@1.9.5: resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} dev: false + /@xstate/react@4.1.2(@types/react@18.2.69)(react@18.2.0)(xstate@5.18.1): + resolution: {integrity: sha512-orAidFrKCrU0ZwN5l/ABPlBfW2ziRDT2RrYoktRlZ0WRoLvA2E/uAC1JpZt43mCLtc8jrdwYCgJiqx1V8NvGTw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + xstate: ^5.18.1 + peerDependenciesMeta: + xstate: + optional: true + dependencies: + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.69)(react@18.2.0) + use-sync-external-store: 1.2.2(react@18.2.0) + xstate: 5.18.1 + transitivePeerDependencies: + - '@types/react' + dev: false + /@xtuc/ieee754@1.2.0: resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -14620,12 +15950,6 @@ packages: tslib: 2.6.2 dev: false - /aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - dependencies: - deep-equal: 2.2.0 - dev: true - /aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} dependencies: @@ -15082,6 +16406,10 @@ packages: is-windows: 1.0.2 dev: false + /big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + dev: false + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -15426,6 +16754,14 @@ packages: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + /chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: false + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -15877,6 +17213,11 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: false + /copy-anything@3.0.5: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} @@ -16126,6 +17467,10 @@ packages: engines: {node: '>= 6'} dev: true + /css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: false + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -16392,28 +17737,6 @@ packages: engines: {node: '>=6'} dev: true - /deep-equal@2.2.0: - resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} - dependencies: - call-bind: 1.0.2 - es-get-iterator: 1.1.2 - get-intrinsic: 1.1.3 - is-arguments: 1.1.1 - is-array-buffer: 3.0.1 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - isarray: 2.0.5 - object-is: 1.1.5 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.9 - dev: true - /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -16613,6 +17936,10 @@ packages: resolution: {integrity: sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==} dev: true + /dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dev: false + /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: @@ -16939,19 +18266,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /es-get-iterator@1.1.2: - resolution: {integrity: sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.3 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - dev: true - /es-module-lexer@1.3.1: resolution: {integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==} @@ -19684,10 +20998,6 @@ packages: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: false - /is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - dev: true - /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -19757,10 +21067,6 @@ packages: call-bind: 1.0.2 has-tostringtag: 1.0.0 - /is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - dev: true - /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: @@ -19831,22 +21137,11 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - /is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - dev: true - /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.2 - /is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.3 - dev: true - /is-what@4.1.16: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} @@ -21795,14 +23090,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - dev: true - /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -22460,6 +23747,10 @@ packages: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} dev: false + /performant-array-to-tree@1.11.0: + resolution: {integrity: sha512-YwCqIDvnaebXaKuKQhI5yJD6ryDc3FxvoeX/5ougXTKDUWb7s5S2BuBgIyftCa4sBe1+ZU5Kmi4RJy+pjjjrpw==} + dev: false + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -23537,6 +24828,53 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /react-aria@3.34.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSprEI5EojDFCm357MxnKAxJZN68OYIt6UH6N0KCo6MEUAVZMbhMSmGYjw/kLK4rI7KrbJDqGqUMQkwc93W9Ng==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + dependencies: + '@internationalized/string': 3.2.3 + '@react-aria/breadcrumbs': 3.5.16(react@18.2.0) + '@react-aria/button': 3.9.8(react@18.2.0) + '@react-aria/calendar': 3.5.11(react-dom@18.2.0)(react@18.2.0) + '@react-aria/checkbox': 3.14.6(react@18.2.0) + '@react-aria/combobox': 3.10.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/datepicker': 3.11.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/dialog': 3.5.17(react-dom@18.2.0)(react@18.2.0) + '@react-aria/dnd': 3.7.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/focus': 3.18.2(react@18.2.0) + '@react-aria/gridlist': 3.9.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/i18n': 3.12.2(react@18.2.0) + '@react-aria/interactions': 3.22.2(react@18.2.0) + '@react-aria/label': 3.7.11(react@18.2.0) + '@react-aria/link': 3.7.4(react@18.2.0) + '@react-aria/listbox': 3.13.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/menu': 3.15.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/meter': 3.4.16(react@18.2.0) + '@react-aria/numberfield': 3.11.6(react-dom@18.2.0)(react@18.2.0) + '@react-aria/overlays': 3.23.2(react-dom@18.2.0)(react@18.2.0) + '@react-aria/progress': 3.4.16(react@18.2.0) + '@react-aria/radio': 3.10.7(react@18.2.0) + '@react-aria/searchfield': 3.7.8(react@18.2.0) + '@react-aria/select': 3.14.9(react-dom@18.2.0)(react@18.2.0) + '@react-aria/selection': 3.19.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/separator': 3.4.2(react@18.2.0) + '@react-aria/slider': 3.7.11(react@18.2.0) + '@react-aria/ssr': 3.9.5(react@18.2.0) + '@react-aria/switch': 3.6.7(react@18.2.0) + '@react-aria/table': 3.15.3(react-dom@18.2.0)(react@18.2.0) + '@react-aria/tabs': 3.9.5(react-dom@18.2.0)(react@18.2.0) + '@react-aria/tag': 3.4.5(react-dom@18.2.0)(react@18.2.0) + '@react-aria/textfield': 3.14.8(react@18.2.0) + '@react-aria/tooltip': 3.7.7(react@18.2.0) + '@react-aria/utils': 3.25.2(react@18.2.0) + '@react-aria/visually-hidden': 3.8.15(react@18.2.0) + '@react-types/shared': 3.24.1(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-collapse@5.1.1(react@18.2.0): resolution: {integrity: sha512-k6cd7csF1o9LBhQ4AGBIdxB60SUEUMQDAnL2z1YvYNr9KoKr+nDkhN6FK7uGaBd/rYrYfrMpzpmJEIeHRYogBw==} peerDependencies: @@ -23945,6 +25283,28 @@ packages: tslib: 2.5.0 dev: false + /react-window-splitter@0.4.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-7XjNxlsBqOySyPEf32GZXLIA7gIPLWqswnOwM4Aw15qiMChMm8einkz40RbLfvrD11EKHIU35vcUH1RDA+we9w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.69)(react@18.2.0) + '@testing-library/jest-dom': 6.5.0 + '@window-splitter/state': 0.4.1 + '@xstate/react': 4.1.2(@types/react@18.2.69)(react@18.2.0)(xstate@5.18.1) + invariant: 2.2.4 + react: 18.2.0 + react-aria: 3.34.3(react-dom@18.2.0)(react@18.2.0) + react-dom: 18.2.0(react@18.2.0) + reforest: 0.13.0(@types/react@18.2.69)(react@18.2.0) + xstate: 5.18.1 + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -24102,6 +25462,19 @@ packages: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} dev: false + /reforest@0.13.0(@types/react@18.2.69)(react@18.2.0): + resolution: {integrity: sha512-f0It/s51f1UWCCCni0viULALDBhxWBPFnLmZRYtKcz4zYeNWqeNTdcnU/OpBry9tk+jyMQcH3MLK8UdzsAvA5w==} + peerDependencies: + react: '>=16.8' + dependencies: + performant-array-to-tree: 1.11.0 + react: 18.2.0 + zustand: 4.5.5(@types/react@18.2.69)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + /regenerate-unicode-properties@10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} engines: {node: '>=4'} @@ -26846,6 +28219,13 @@ packages: unist-util-visit-parents: 5.1.1 dev: true + /universal-cookie@7.2.0: + resolution: {integrity: sha512-PvcyflJAYACJKr28HABxkGemML5vafHmiL4ICe3e+BEKXRMt0GaFLZhAwgv637kFFnnfiSJ8e6jknrKkMrU+PQ==} + dependencies: + '@types/cookie': 0.6.0 + cookie: 0.6.0 + dev: false + /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -27854,15 +29234,6 @@ packages: is-string: 1.0.7 is-symbol: 1.0.4 - /which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - dev: true - /which-module@2.0.0: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: false @@ -28158,6 +29529,10 @@ packages: engines: {node: '>=0.4.0'} dev: false + /xstate@5.18.1: + resolution: {integrity: sha512-m02IqcCQbaE/kBQLunwub/5i8epvkD2mFutnL17Oeg1eXTShe1sRF4D5mhv1dlaFO4vbW5gRGRhraeAD5c938g==} + dev: false + /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -28307,6 +29682,26 @@ packages: /zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} + /zustand@4.5.5(@types/react@18.2.69)(react@18.2.0): + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.69 + react: 18.2.0 + use-sync-external-store: 1.2.2(react@18.2.0) + dev: false + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true