From f5fbacd7d2352afe766cd73d850fd24251ad6dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Mu=CC=88ller?= Date: Fri, 11 Aug 2023 16:11:29 +0200 Subject: [PATCH] feat: improve ui --- .changeset/five-icons-sparkle.md | 5 + .changeset/gentle-foxes-destroy.md | 5 + .changeset/slimy-countries-help.md | 6 + package.json | 2 + packages/debounce/src/main.ts | 9 +- packages/streams/package.json | 13 +- packages/streams/src/sync.ts | 7 +- packages/streams/vite.config.ts | 2 + packages/tsconfig/cli.json | 1 + packages/tsconfig/next.json | 3 +- packages/turbo-graph-ui/app/globals.css | 120 +++ packages/turbo-graph-ui/app/layout.tsx | 24 + packages/turbo-graph-ui/app/page.tsx | 27 + .../turbo-graph-ui/components/FlowGraph.tsx | 135 ++++ packages/turbo-graph-ui/components/Graph.tsx | 67 ++ .../turbo-graph-ui/components/GraphInputs.tsx | 65 ++ packages/turbo-graph-ui/components/Input.tsx | 25 + packages/turbo-graph-ui/lib/flow.ts | 121 +++ packages/turbo-graph-ui/lib/turbo.ts | 121 +++ packages/turbo-graph-ui/lib/utils.ts | 30 + packages/turbo-graph-ui/next.config.js | 1 - packages/turbo-graph-ui/package.json | 20 +- packages/turbo-graph-ui/pages/_app.tsx | 14 - packages/turbo-graph-ui/pages/api/graph.ts | 101 --- packages/turbo-graph-ui/pages/index.tsx | 170 ----- packages/turbo-graph-ui/styles/globals.css | 39 - packages/turbo-graph-ui/tailwind.config.js | 74 +- packages/turbo-graph-ui/tsconfig.json | 2 +- packages/turbo-graph/README.md | 17 +- packages/turbo-graph/package.json | 2 +- packages/turbo-graph/src/index.ts | 56 +- pnpm-lock.yaml | 693 +++++++++++++++--- 32 files changed, 1493 insertions(+), 484 deletions(-) create mode 100644 .changeset/five-icons-sparkle.md create mode 100644 .changeset/gentle-foxes-destroy.md create mode 100644 .changeset/slimy-countries-help.md create mode 100644 packages/turbo-graph-ui/app/globals.css create mode 100644 packages/turbo-graph-ui/app/layout.tsx create mode 100644 packages/turbo-graph-ui/app/page.tsx create mode 100644 packages/turbo-graph-ui/components/FlowGraph.tsx create mode 100644 packages/turbo-graph-ui/components/Graph.tsx create mode 100644 packages/turbo-graph-ui/components/GraphInputs.tsx create mode 100644 packages/turbo-graph-ui/components/Input.tsx create mode 100644 packages/turbo-graph-ui/lib/flow.ts create mode 100644 packages/turbo-graph-ui/lib/turbo.ts create mode 100644 packages/turbo-graph-ui/lib/utils.ts delete mode 100644 packages/turbo-graph-ui/pages/_app.tsx delete mode 100644 packages/turbo-graph-ui/pages/api/graph.ts delete mode 100644 packages/turbo-graph-ui/pages/index.tsx delete mode 100644 packages/turbo-graph-ui/styles/globals.css diff --git a/.changeset/five-icons-sparkle.md b/.changeset/five-icons-sparkle.md new file mode 100644 index 000000000..542d1636d --- /dev/null +++ b/.changeset/five-icons-sparkle.md @@ -0,0 +1,5 @@ +--- +'@yeger/streams': minor +--- + +implement streams package diff --git a/.changeset/gentle-foxes-destroy.md b/.changeset/gentle-foxes-destroy.md new file mode 100644 index 000000000..4e36ee34f --- /dev/null +++ b/.changeset/gentle-foxes-destroy.md @@ -0,0 +1,5 @@ +--- +'@yeger/debounce': minor +--- + +add optional args to callback diff --git a/.changeset/slimy-countries-help.md b/.changeset/slimy-countries-help.md new file mode 100644 index 000000000..3687f45d9 --- /dev/null +++ b/.changeset/slimy-countries-help.md @@ -0,0 +1,6 @@ +--- +'@yeger/turbo-graph-ui': major +'@yeger/turbo-graph': major +--- + +re-implement visualization diff --git a/package.json b/package.json index d3085bd5f..4ee920cdd 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "bundlesize": "nr turbo run bundlesize", "changeset": "changeset", "ci": "cross-env COVERAGE=true nr turbo run build bundlesize generate lint test check:publish check:tsc check:vue --output-logs=errors-only", + "clean": "rimraf -g **/.tsbuildinfo **/tsconfig.tsbuildinfo", "dev": "nr turbo run dev --parallel", "fix": "nr turbo run lint lint:root -- --fix", "codegen": "nr turbo run codegen", @@ -48,6 +49,7 @@ "husky": "8.0.3", "lint-staged": "13.2.3", "publint": "0.2.0", + "rimraf": "5.0.1", "syncpack": "10.9.3", "turbo": "1.10.12", "vercel": "31.2.3", diff --git a/packages/debounce/src/main.ts b/packages/debounce/src/main.ts index c12d681a6..2193161bd 100644 --- a/packages/debounce/src/main.ts +++ b/packages/debounce/src/main.ts @@ -4,12 +4,15 @@ * @param delay - A delay after which the callback will be invoked. * @returns The debounced callback. */ -export function debounce(cb: () => void, delay?: number) { +export function debounce( + cb: (...args: Args) => void, + delay?: number, +) { let timeout: any - return () => { + return (...args: Args) => { if (timeout !== undefined) { clearTimeout(timeout) } - timeout = setTimeout(() => cb(), delay) + timeout = setTimeout(() => cb(...args), delay) } } diff --git a/packages/streams/package.json b/packages/streams/package.json index 0b73629a2..7954e99ad 100644 --- a/packages/streams/package.json +++ b/packages/streams/package.json @@ -13,9 +13,14 @@ "sideEffects": false, "exports": { ".": { - "types": "./dist/types/index.d.ts", - "require": "./dist/index.umd.js", - "import": "./dist/index.mjs" + "import": { + "types": "./dist/types/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/types/index.d.ts", + "default": "./dist/index.umd.js" + } } }, "main": "dist/index.umd.js", @@ -38,7 +43,7 @@ "devDependencies": { "@yeger/tsconfig": "workspace:*", "typescript": "5.1.6", - "vite": "4.4.4", + "vite": "4.4.9", "vite-plugin-lib": "workspace:*" }, "publishConfig": { diff --git a/packages/streams/src/sync.ts b/packages/streams/src/sync.ts index 64c2dd7f4..472b52763 100644 --- a/packages/streams/src/sync.ts +++ b/packages/streams/src/sync.ts @@ -28,8 +28,11 @@ export abstract class Stream implements Iterable { return new Map(stream) } - public toRecord(fn: Processor): Record { - return Object.fromEntries(this.map((x) => [fn(x), x] as const)) + public toRecord( + key: Processor, + value: Processor, + ): Record { + return Object.fromEntries(this.map((x) => [key(x), value?.(x)] as const)) } public abstract [Symbol.iterator](): IterableIterator diff --git a/packages/streams/vite.config.ts b/packages/streams/vite.config.ts index b777de60a..eb7aef4b8 100644 --- a/packages/streams/vite.config.ts +++ b/packages/streams/vite.config.ts @@ -1,3 +1,5 @@ +import process from 'node:process' + import { defineConfig } from 'vite' import { library } from 'vite-plugin-lib' diff --git a/packages/tsconfig/cli.json b/packages/tsconfig/cli.json index 4f5f86b09..52f5089a7 100644 --- a/packages/tsconfig/cli.json +++ b/packages/tsconfig/cli.json @@ -3,6 +3,7 @@ "compilerOptions": { "declaration": false, "module": "CommonJS", + "noEmit": false, "sourceMap": false } } diff --git a/packages/tsconfig/next.json b/packages/tsconfig/next.json index f4bc40de7..189a59083 100644 --- a/packages/tsconfig/next.json +++ b/packages/tsconfig/next.json @@ -2,6 +2,7 @@ "extends": "./web.json", "compilerOptions": { "noEmit": true, - "incremental": true + "incremental": true, + "plugins": [{ "name": "next" }] } } diff --git a/packages/turbo-graph-ui/app/globals.css b/packages/turbo-graph-ui/app/globals.css new file mode 100644 index 000000000..66116c863 --- /dev/null +++ b/packages/turbo-graph-ui/app/globals.css @@ -0,0 +1,120 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 0 0% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 4.9%; + + --primary: 0 0% 11.2%; + --primary-foreground: 0 0% 98%; + + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 11.2%; + + --muted: 0 0% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 0 0% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 0 0% 0%; + --foreground: 0 0% 98%; + + --card: 0 0% 4.9%; + --card-foreground: 0 0% 98%; + + --popover: 0 0% 4.9%; + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + --primary-foreground: 0 0% 11.2%; + + --secondary: 0 0% 17.5%; + --secondary-foreground: 0 0% 98%; + + --muted: 0 0% 17.5%; + --muted-foreground: 0 0% 65.1%; + + --accent: 0 0% 17.5%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 0% 30.6%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 17.5%; + --input: 0 0% 17.5%; + --ring: hsl(212.7, 0%, 83.9); + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +.react-flow__edge { + pointer-events: none !important; +} + +.react-flow__controls button { + background-color: var(--bg-color); + color: var(--text-color); + border: 1px solid hsl(var(--foreground)); + border-bottom: none; +} + +.react-flow__controls button:hover { + background-color: hsl(var(--secondary)); +} + +.react-flow__controls button:first-child { + border-radius: 5px 5px 0 0; +} + +.react-flow__controls button:last-child { + border-bottom: 1px solid hsl(var(--foreground)); + border-radius: 0 0 5px 5px; +} + +.react-flow__controls button path { + fill: hsl(var(--foreground)); +} + +.react-flow__controls button:disabled path { + fill: hsl(var(--muted-foreground)); +} + +.react-flow__attribution { + background: hsl(var(--background)) !important; +} + +.react-flow__attribution a { + color: hsl(var(--muted-foreground)) !important; +} + +.react-flow__minimap { + background: hsl(var(--background)) !important; +} diff --git a/packages/turbo-graph-ui/app/layout.tsx b/packages/turbo-graph-ui/app/layout.tsx new file mode 100644 index 000000000..8eef6b619 --- /dev/null +++ b/packages/turbo-graph-ui/app/layout.tsx @@ -0,0 +1,24 @@ +import { FilterInput, TaskInput } from '../components/GraphInputs' +import './globals.css' + +export default function RootLayout({ + // Layouts must accept a children prop. + // This will be populated with nested layouts or pages + children, +}: { + children: React.ReactNode +}) { + return ( + + +
+
+ + +
+ {children} +
+ + + ) +} diff --git a/packages/turbo-graph-ui/app/page.tsx b/packages/turbo-graph-ui/app/page.tsx new file mode 100644 index 000000000..5d63491aa --- /dev/null +++ b/packages/turbo-graph-ui/app/page.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from 'next' + +import { Graph } from '../components/Graph' + +export const metadata: Metadata = { + title: 'Turbo Graph', + description: 'Interactive visualization of Turborepo task graphs.', + icons: ['/favicon.ico'], +} + +export default async function Home({ + searchParams, +}: { + searchParams?: { [key: string]: string | string[] | undefined } +}) { + const rawTasks = searchParams?.tasks + const tasks = Array.isArray(rawTasks) + ? rawTasks + : rawTasks?.replaceAll(',', ' ')?.split(' ') ?? ['build'] + const filter = searchParams?.filter + + if (filter && Array.isArray(filter)) { + throw new Error(`Unsupported filter ${filter}`) + } + + return +} diff --git a/packages/turbo-graph-ui/components/FlowGraph.tsx b/packages/turbo-graph-ui/components/FlowGraph.tsx new file mode 100644 index 000000000..5203bc7b4 --- /dev/null +++ b/packages/turbo-graph-ui/components/FlowGraph.tsx @@ -0,0 +1,135 @@ +'use client' + +import { Stream } from '@yeger/streams' +import { scaleOrdinal } from 'd3-scale' +import { schemeSet3 } from 'd3-scale-chromatic' +import type { ReactNode } from 'react' +import { useEffect, useMemo } from 'react' +import { + Background, + Controls, + Handle, + MiniMap, + Position, + ReactFlow, + useReactFlow, +} from 'reactflow' + +import 'reactflow/dist/style.css' +import { + type FlowNode, + TASK_HEIGHT_VAR, + TASK_WIDTH_VAR, + convertGraph, + getTaskColorVar, +} from '../lib/flow' +import type { TurboGraph } from '../lib/turbo' +import { useGraphSettings } from '../lib/utils' + +export interface Props { + children: ReactNode + graph: TurboGraph + uniqueTasks: Set +} + +interface TaskProps { + data: FlowNode +} + +function Task({ data }: TaskProps) { + const { task, package: workspace, isTerminal, isOrigin } = data + return ( +
+ {isOrigin ? null : ( + + )} +
+
{task}
+
{workspace}
+
+ {isTerminal ? null : ( + + )} +
+ ) +} + +const nodeTypes = { + task: Task, +} + +export function FlowGraph({ children, graph, uniqueTasks }: Props) { + const { flowGraph, taskCssVars } = useMemo(() => { + const flowGraph = convertGraph(graph) + const getColor = scaleOrdinal(schemeSet3).domain(uniqueTasks) + const taskColors = Stream.from(uniqueTasks).toRecord( + (task) => getTaskColorVar(task), + (task) => getColor(task), + ) + const taskSize: Record = { + [TASK_WIDTH_VAR]: `${flowGraph.sizeConfig.width}px`, + [TASK_HEIGHT_VAR]: `${flowGraph.sizeConfig.height}px`, + } + const taskCssVars = { ...taskColors, ...taskSize } + return { + flowGraph, + taskCssVars, + } + }, [graph, uniqueTasks]) + + const { setParameter } = useGraphSettings() + + const onNodeClicked = (_: unknown, { data }: { data: FlowNode }) => { + setParameter('filter', data.package) + } + + return ( + + {children} + + + + `var(${getTaskColorVar(data.task)})` + } + nodeStrokeColor="#000000" + /> + + + ) +} + +function ViewFitter({ graph }: { graph: TurboGraph }) { + const reactFlow = useReactFlow() + useEffect(() => { + setTimeout(() => { + reactFlow.fitView({ duration: 200 }) + }, 200) + }, [reactFlow, graph]) + return null +} diff --git a/packages/turbo-graph-ui/components/Graph.tsx b/packages/turbo-graph-ui/components/Graph.tsx new file mode 100644 index 000000000..67ee7289f --- /dev/null +++ b/packages/turbo-graph-ui/components/Graph.tsx @@ -0,0 +1,67 @@ +import { Stream } from '@yeger/streams' + +import { getEdgeGradient, getTaskColorVar } from '../lib/flow' +import { getGraph } from '../lib/turbo' + +import { FlowGraph } from './FlowGraph' + +export interface GraphProps { + tasks: string[] + filter?: string +} + +export async function Graph({ tasks, filter }: GraphProps) { + const graphResult = await getGraph(tasks, filter) + + if (graphResult.isError) { + const error = graphResult.getError() + const searchTerm = 'error preparing engine:' + const searchIndex = error.message.lastIndexOf(searchTerm) + const message = + searchIndex > 0 + ? error.message.substring(searchIndex + searchTerm.length) + : error.message + return ( +
+ {message} +
+ ) + } + + const graph = graphResult.get() + const uniqueTasks = Stream.from(graph.nodes) + .map(({ task }) => task) + .toSet() + const edgeGradients = Stream.from(uniqueTasks).flatMap((source) => + Stream.from(uniqueTasks).map((target) => ({ + id: getEdgeGradient(source, target), + sourceColor: `var(${getTaskColorVar(source)})`, + targetColor: `var(${getTaskColorVar(target)})`, + })), + ) + + return ( +
+ + + + {edgeGradients.map(({ id, sourceColor, targetColor }) => ( + + + + + ))} + + + +
+ ) +} diff --git a/packages/turbo-graph-ui/components/GraphInputs.tsx b/packages/turbo-graph-ui/components/GraphInputs.tsx new file mode 100644 index 000000000..5aa686c1d --- /dev/null +++ b/packages/turbo-graph-ui/components/GraphInputs.tsx @@ -0,0 +1,65 @@ +'use client' + +import { debounce } from '@yeger/debounce' +import { type ChangeEventHandler, useEffect, useRef } from 'react' + +import { Input } from '../components/Input' +import type { GraphParameter } from '../lib/utils' +import { useGraphSettings } from '../lib/utils' + +interface GraphInputProps { + defaultValue?: string +} + +interface GraphParameterInputProps extends GraphInputProps { + param: GraphParameter + placeholder: string +} + +function GraphParameterInput({ + param, + placeholder, + defaultValue, +}: GraphParameterInputProps) { + const { getParameter, setParameter } = useGraphSettings() + const urlParameter = getParameter(param) + const onChange: ChangeEventHandler = debounce( + (event) => setParameter(param, event.target.value), + 500, + ) + const ref = useRef(null) + useEffect(() => { + const newValue = urlParameter ?? defaultValue + if (ref.current && newValue !== undefined) { + ref.current.value = newValue + } + }, [ref, param, defaultValue, urlParameter]) + return ( + + ) +} + +export function TaskInput({ defaultValue }: GraphInputProps) { + return ( + + ) +} + +export function FilterInput({ defaultValue }: GraphInputProps) { + return ( + + ) +} diff --git a/packages/turbo-graph-ui/components/Input.tsx b/packages/turbo-graph-ui/components/Input.tsx new file mode 100644 index 000000000..1188d98b5 --- /dev/null +++ b/packages/turbo-graph-ui/components/Input.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react' + +import { cn } from '../lib/utils' + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + }, +) +Input.displayName = 'Input' + +export { Input } diff --git a/packages/turbo-graph-ui/lib/flow.ts b/packages/turbo-graph-ui/lib/flow.ts new file mode 100644 index 000000000..ba6ddeaa6 --- /dev/null +++ b/packages/turbo-graph-ui/lib/flow.ts @@ -0,0 +1,121 @@ +import { Stream } from '@yeger/streams' +import { graphStratify, sugiyama } from 'd3-dag' +import { type Edge, type Node } from 'reactflow' + +import type { TurboEdge, TurboGraph, TurboNode } from './turbo' + +export type FlowGraph = ReturnType + +export interface SizeConfig { + width: number + height: number + horizontalSpacing: number + verticalSpacing: number +} + +export function convertGraph(graph: TurboGraph) { + const hierarchy = createHierarchy(graph) + const longestLine = getLongestLineLength(graph) + const sizeConfig = createSizeConfig(longestLine) + return createFlowGraph(hierarchy, graph.edges, sizeConfig) +} + +function createHierarchy(graph: TurboGraph) { + const stratify = graphStratify() + return stratify([ + ...graph.nodes.map((node) => ({ + ...node, + id: node.id, + parentIds: graph.edges + .filter((edge) => edge.target === node.id) + .map(({ source }) => source), + })), + ]) +} + +function getLongestLineLength({ nodes }: TurboGraph) { + const length = Math.max( + ...Stream.from(nodes).flatMap(({ task, package: workspace }) => [ + task.length, + workspace.length, + ]), + ) + if (length < 0) { + return 1 + } + return length +} + +function createSizeConfig(longestLine: number): SizeConfig { + return { + width: longestLine * 10, + height: 80, + horizontalSpacing: 128, + verticalSpacing: 128, + } +} + +export interface FlowNode extends TurboNode { + isOrigin: boolean + isTerminal: boolean +} + +function createFlowGraph( + hierarchy: ReturnType, + turboEdges: TurboEdge[], + sizeConfig: SizeConfig, +) { + const { width, height, horizontalSpacing, verticalSpacing } = sizeConfig + const layout = sugiyama().nodeSize([ + width + horizontalSpacing, + height + verticalSpacing, + ]) + const layoutResult = layout(hierarchy) + const nodes = Stream.from(hierarchy.nodes()) + .map>( + (node) => + ({ + id: node.data.id, + data: { + ...node.data, + isTerminal: node.nchildren() === 0, + isOrigin: node.nparents() === 0, + }, + position: { x: node.x, y: node.y }, + type: 'task', + }) as const, + ) + .toArray() + const edges = turboEdges.map>((edge) => ({ + id: `edge-${edge.source}-${edge.target}`, + source: edge.source, + target: edge.target, + animated: true, + style: { + stroke: `url(#${getEdgeGradient(edge.sourceTask, edge.targetTask)})`, + width: 4, + }, + })) + return { nodes, edges, sizeConfig, layoutResult } +} + +function normalizeTaskName(task: string) { + return task + .replaceAll(':', '-') + .replaceAll('#', '-') + .replaceAll('/', '-') + .replaceAll('@', '-') +} + +export function getEdgeGradient(sourceTask: string, targetTask: string) { + return `edge-gradient-${normalizeTaskName(sourceTask)}-${normalizeTaskName( + targetTask, + )}` +} + +export function getTaskColorVar(task: string) { + return `--task-color-${normalizeTaskName(task)}` +} + +export const TASK_WIDTH_VAR = '--task-width' +export const TASK_HEIGHT_VAR = '--task-height' diff --git a/packages/turbo-graph-ui/lib/turbo.ts b/packages/turbo-graph-ui/lib/turbo.ts new file mode 100644 index 000000000..a35ef52b4 --- /dev/null +++ b/packages/turbo-graph-ui/lib/turbo.ts @@ -0,0 +1,121 @@ +import fs from 'node:fs/promises' +import path from 'node:path' + +import { execa } from 'execa' +import type { Result } from 'resumon' +import { err, ok } from 'resumon' + +interface Data { + dir: string + config: Config +} + +interface Config { + pipeline: Record +} + +export async function getGraph( + tasks: string[], + filter?: string, +): Promise> { + const filteredTasks = tasks.filter(Boolean) + if (filteredTasks.length === 0) { + return ok({ nodes: [], edges: [] }) + } + const { dir } = await findTurboConfig() + const stdout = await executeCommand(dir, filteredTasks, filter) + return stdout.map(createGraph) +} + +async function executeCommand( + dir: string, + tasks: string[], + filter?: string, +): Promise> { + // TODO: Get .bin dir location from package manager + try { + const args = ['run', ...tasks, '--concurrency=100%', '--dry=json'] + if (filter) { + args.push(`--filter=${filter}`) + } + const { stdout } = await execa( + `node_modules${path.sep}.bin${path.sep}turbo`, + args, + { + cwd: dir, + }, + ) + return ok(stdout) + } catch (error) { + return err(error as Error) + } +} + +async function findTurboConfig(currentPath = '.'): Promise { + const files = await fs.readdir(currentPath) + const turboConfig = files.find((file) => file === 'turbo.json') + if (!turboConfig) { + return findTurboConfig(`..${path.sep}${currentPath}`) + } + const file = `${currentPath}${path.sep}${turboConfig}` + const buffer = await fs.readFile(file) + const config = JSON.parse(buffer.toString()) + return { dir: currentPath, config } +} + +export interface TurboNode { + id: string + package: string + task: string +} + +export interface TurboEdge { + source: string + sourceTask: string + target: string + targetTask: string +} + +export interface TurboGraph { + nodes: TurboNode[] + edges: TurboEdge[] +} + +export interface TurboCLIJson { + tasks: { + taskId: string + task: string + package: string + inputs: unknown + outputs: unknown[] + directory: string + dependencies: string[] + dependents: string[] + command: string + framework: string + }[] +} + +function createGraph(input: string): TurboGraph { + const data = JSON.parse(input) as TurboCLIJson + const tasks = data.tasks.filter(({ command }) => command !== '') + + const nodes: TurboNode[] = tasks.map((task) => ({ + id: task.taskId, + package: task.package, + task: task.task, + })) + const validTasks = new Set(tasks.map(({ taskId }) => taskId)) + + const edges: TurboEdge[] = tasks.flatMap(({ taskId, dependencies }) => + dependencies + .filter((dependency) => validTasks.has(dependency)) + .map((dependency) => ({ + source: dependency, + sourceTask: dependency.substring(dependency.indexOf('#') + 1), + target: taskId, + targetTask: taskId.substring(taskId.indexOf('#') + 1), + })), + ) + return { nodes, edges } +} diff --git a/packages/turbo-graph-ui/lib/utils.ts b/packages/turbo-graph-ui/lib/utils.ts new file mode 100644 index 000000000..00fe39c91 --- /dev/null +++ b/packages/turbo-graph-ui/lib/utils.ts @@ -0,0 +1,30 @@ +import { Stream } from '@yeger/streams' +import { type ClassValue, clsx } from 'clsx' +import { useRouter, useSearchParams } from 'next/navigation' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} + +export type GraphParameter = 'tasks' | 'filter' + +export function useGraphSettings() { + const router = useRouter() + const searchParams = useSearchParams() + + function setParameter(param: GraphParameter, newValue: string | null) { + const newSearchParams = Stream.from(searchParams.entries()) + .filter(([key]) => key !== param) + .concat(newValue ? [[param, newValue]] : []) + .map(([key, value]) => `${key}=${value}`) + .join('&') + router.push(`/?${newSearchParams}`) + } + + function getParameter(param: GraphParameter) { + return searchParams.get(param) + } + + return { setParameter, getParameter } +} diff --git a/packages/turbo-graph-ui/next.config.js b/packages/turbo-graph-ui/next.config.js index 1160d3cd4..ae887958d 100644 --- a/packages/turbo-graph-ui/next.config.js +++ b/packages/turbo-graph-ui/next.config.js @@ -1,4 +1,3 @@ -// eslint-disable-next-line tsdoc/syntax /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, diff --git a/packages/turbo-graph-ui/package.json b/packages/turbo-graph-ui/package.json index ae9728709..da602164a 100644 --- a/packages/turbo-graph-ui/package.json +++ b/packages/turbo-graph-ui/package.json @@ -20,20 +20,30 @@ "scripts": { "build": "next build", "check:publish": "publint run --strict", - "dev": "next dev", - "lint": "next lint" + "dev": "next dev --turbo", + "lint": "next lint", + "preview": "next start" }, "peerDependencies": { "turbo": "^1.0.0" }, "dependencies": { - "@tanstack/react-query": "4.32.6", + "@yeger/debounce": "workspace:*", + "@yeger/streams": "workspace:*", + "class-variance-authority": "0.7.0", + "clsx": "2.0.0", + "d3-dag": "1.0.0", "d3-graph-controller": "workspace:*", "d3-scale": "4.0.2", "d3-scale-chromatic": "3.0.0", "execa": "7.2.0", + "lucide-react": "^0.268.0", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "reactflow": "11.8.1", + "resumon": "workspace:*", + "tailwind-merge": "^1.14.0", + "tailwindcss-animate": "^1.0.6" }, "devDependencies": { "@types/d3-scale": "4.0.3", @@ -44,7 +54,7 @@ "@yeger/tsconfig": "workspace:*", "autoprefixer": "10.4.14", "eslint-config-next": "13.4.13", - "next": "13.4.13", + "next": "13.4.16", "postcss": "8.4.27", "tailwindcss": "3.3.3", "typescript": "5.1.6" diff --git a/packages/turbo-graph-ui/pages/_app.tsx b/packages/turbo-graph-ui/pages/_app.tsx deleted file mode 100644 index b9a713549..000000000 --- a/packages/turbo-graph-ui/pages/_app.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import '../styles/globals.css' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import type { AppProps } from 'next/app' -import 'd3-graph-controller/default.css' - -const queryClient = new QueryClient() - -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - ) -} diff --git a/packages/turbo-graph-ui/pages/api/graph.ts b/packages/turbo-graph-ui/pages/api/graph.ts deleted file mode 100644 index 4d70806a7..000000000 --- a/packages/turbo-graph-ui/pages/api/graph.ts +++ /dev/null @@ -1,101 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import fs from 'node:fs/promises' -import path from 'node:path' - -import { execa } from 'execa' -import type { NextApiRequest, NextApiResponse } from 'next' - -interface Data { - dir: string - config: Config -} - -interface Config { - pipeline: Record -} - -export default async function handler( - _: NextApiRequest, - res: NextApiResponse, -) { - const { dir, config } = await findTurboConfig() - const tasks = getTask(config) - const stdout = await executeCommand(tasks, dir) - const graph = await processResult(stdout) - res.status(200).json(graph) -} - -async function executeCommand(tasks: string[], dir: string): Promise { - // TODO: Get .bin dir location from package manager - const { stdout } = await execa( - `node_modules${path.sep}.bin${path.sep}turbo`, - ['run', ...tasks, '--concurrency=100%', '--graph'], - { - cwd: dir, - }, - ) - return stdout -} - -function getTask(config: Config): string[] { - const pipeline = Object.keys(config.pipeline).map((entry) => { - if (!entry.includes('#')) { - return entry - } - return entry.substring(entry.indexOf('#') + 1) - }) - - return [...new Set(pipeline)] -} - -async function findTurboConfig(currentPath = '.'): Promise { - const files = await fs.readdir(currentPath) - const turboConfig = files.find((file) => file === 'turbo.json') - if (!turboConfig) { - return findTurboConfig(`..${path.sep}${currentPath}`) - } - const file = `${currentPath}${path.sep}${turboConfig}` - const buffer = await fs.readFile(file) - const config = JSON.parse(buffer.toString()) - return { dir: currentPath, config } -} - -export interface TurboNode { - id: string - workspace: string - task: string -} - -export interface TurboEdge { - source: string - target: string -} - -export interface TurboGraph { - nodes: TurboNode[] - edges: TurboEdge[] -} - -async function processResult(input: string): Promise { - const edges: TurboEdge[] = input - .split('\n') - .filter((line) => line.includes('->') && !line.includes('___ROOT___')) - .map((line) => line.substring(line.indexOf('"') + 1, line.lastIndexOf('"'))) - .map((line) => { - const [source, target] = line.split('" -> "', 2) - return { source: source!, target: target! } - }) - const nodes: TurboNode[] = [...new Set(edges.flatMap(Object.values))].map( - (id) => { - const splitIndex = id.lastIndexOf('#') - const workspace = id.substring(0, splitIndex).replace('[root] ', '') - const task = id.substring(splitIndex + 1, id.length) - return { - id, - workspace, - task, - } - }, - ) - return { nodes, edges } -} diff --git a/packages/turbo-graph-ui/pages/index.tsx b/packages/turbo-graph-ui/pages/index.tsx deleted file mode 100644 index 2958b3451..000000000 --- a/packages/turbo-graph-ui/pages/index.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { - GraphController, - Markers, - PositionInitializers, - defineGraph, - defineGraphConfig, - defineLink, - defineNodeWithDefaults, -} from 'd3-graph-controller' -import { scaleOrdinal } from 'd3-scale' -import { schemeSet3 } from 'd3-scale-chromatic' -import Head from 'next/head' -import { useEffect, useMemo, useRef, useState } from 'react' - -import type { TurboGraph } from './api/graph' - -export default function Home() { - const [, setTrigger] = useState(0) - const trigger = () => setTrigger((val) => val + 1) - const query = useQuery({ - queryKey: ['graph'], - queryFn: async () => { - const res = await fetch('/api/graph') - return (await res.json()) as TurboGraph - }, - }) - - const graphRef = useRef(null) - const graphData = query.data - - const colors = useMemo(() => { - const tasks = [ - ...new Set(graphData?.nodes.map(({ task }) => task) ?? []), - ].sort() - return scaleOrdinal(schemeSet3).domain(tasks) - }, [graphData]) - - const graphController = useMemo(() => { - const container = graphRef.current - if (!container || !graphData) { - return undefined - } - const nodes = graphData.nodes.map((node) => - defineNodeWithDefaults({ - id: node.id, - type: node.task, - color: colors(node.task), - label: { text: node.workspace, color: 'black', fontSize: '0.875rem' }, - }), - ) - const links = graphData.edges.map((edge) => { - const source = nodes.find((node) => node.id === edge.source)! - const target = nodes.find((node) => node.id === edge.target)! - return defineLink({ - source, - target, - color: '#aaa', - label: false, - }) - }) - - return new GraphController( - container, - defineGraph({ nodes, links }), - defineGraphConfig({ - autoResize: true, - hooks: { - afterZoom(scale: number, xOffset: number, yOffset: number) { - container.style.setProperty('--offset-x', `${xOffset}px`) - container.style.setProperty('--offset-y', `${yOffset}px`) - container.style.setProperty('--dot-size', `${scale}rem`) - }, - }, - marker: Markers.Arrow(4), - positionInitializer: - nodes.length > 1 - ? PositionInitializers.Randomized - : PositionInitializers.Centered, - simulation: { - forces: { - link: { length: 200 }, - charge: { - strength: 200, - }, - collision: { - radiusMultiplier: 10, - strength: 300, - }, - }, - }, - zoom: { - min: 0.3, - max: 2, - }, - }), - ) - }, [colors, graphRef, graphData]) - - useEffect(() => { - return () => { - graphController?.shutdown() - } - }, [graphController]) - - const tasks = graphController?.nodeTypes.sort() ?? [] - - return ( -
- - Turbo Graph - - - - -
-
-
- {tasks.map((task) => ( -
- { - graphController?.filterNodesByType( - event.target.checked, - task, - ) - trigger() - }} - /> - -
-
- ))} -
-
-
- - -
-
-
- {!graphController ? ( -
- Loading -
- ) : null} -
-
-
-
- ) -} diff --git a/packages/turbo-graph-ui/styles/globals.css b/packages/turbo-graph-ui/styles/globals.css deleted file mode 100644 index e2545e4d4..000000000 --- a/packages/turbo-graph-ui/styles/globals.css +++ /dev/null @@ -1,39 +0,0 @@ -html, -body { - padding: 0; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, - Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -} - -html, -body, -#__next { - width: 100vw; - height: 100vh; -} - -.bg-dotted { - --color-dots: #eeee; - --dot-size: 1rem; - background-image: radial-gradient(var(--color-dots) 15%, transparent 16%); - background-size: var(--dot-size) var(--dot-size); - background-position: var(--offset-x, 0) var(--offset-y, 0); -} - -a { - color: inherit; - text-decoration: none; -} - -* { - box-sizing: border-box; -} - -.node__label { - transform: translateY(-1.5rem); -} - -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/packages/turbo-graph-ui/tailwind.config.js b/packages/turbo-graph-ui/tailwind.config.js index cc2cb9261..9507af715 100644 --- a/packages/turbo-graph-ui/tailwind.config.js +++ b/packages/turbo-graph-ui/tailwind.config.js @@ -1,12 +1,76 @@ -// eslint-disable-next-line tsdoc/syntax /** @type {import('tailwindcss').Config} */ module.exports = { + darkMode: ['class'], content: [ - './pages/**/*.{js,ts,jsx,tsx}', - './components/**/*.{js,ts,jsx,tsx}', + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', ], theme: { - extend: {}, + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + keyframes: { + 'accordion-down': { + from: { height: 0 }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: 0 }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, }, - plugins: [], + plugins: [require('tailwindcss-animate')], } diff --git a/packages/turbo-graph-ui/tsconfig.json b/packages/turbo-graph-ui/tsconfig.json index aec81db77..312d932ec 100644 --- a/packages/turbo-graph-ui/tsconfig.json +++ b/packages/turbo-graph-ui/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "@yeger/tsconfig/next", - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } diff --git a/packages/turbo-graph/README.md b/packages/turbo-graph/README.md index 4c92a419f..fcc138a52 100644 --- a/packages/turbo-graph/README.md +++ b/packages/turbo-graph/README.md @@ -29,24 +29,23 @@ For simple usage, create a script in your root `package.json`. } ``` -Nodes represent your workspaces, i.e., packages. -Each workspace can have multiple nodes, each color defining the corresponding task (as seen in the toolbar). +Nodes represent your packages. +Each package can have multiple nodes, each color defining the corresponding task. ### CLI +Optionally, the CLI command can be followed by a list of tasks names that are loaded at startup. +E.g., `turbo-graph build test` will load `build` and `test` tasks. +Like the `--filter` option listed below, this only applies if the `--open` open option is used. + +- `-f, --filter `: Filter nodes by a Turborepo filter (e.g., `my-lib...`). - `-o, --open`: Open the visualizer in the default browser. - `-p, --port `: Port of the visualizer. (default: 29312) - `-h, --help`: Display help message ### Controls -By right-clicking a node, only its (transitive) dependencies and dependents are shown. - -Further, the checkboxes allow filtering nodes by their corresponding tasks. - -The graph can be zoomed using the mouse-wheel and nodes may be dragged while holding the left mouse-button. - -The "Reset" button centers the graph. +By double-clicking a node, its package is used as the filter. ## License diff --git a/packages/turbo-graph/package.json b/packages/turbo-graph/package.json index 90d5499c0..30732d8cc 100644 --- a/packages/turbo-graph/package.json +++ b/packages/turbo-graph/package.json @@ -29,7 +29,7 @@ "dependencies": { "@yeger/turbo-graph-ui": "workspace:*", "cac": "6.7.14", - "next": "13.4.13", + "next": "13.4.16", "open": "8.4.2", "picocolors": "1.0.0" }, diff --git a/packages/turbo-graph/src/index.ts b/packages/turbo-graph/src/index.ts index b6e17e78e..17a416e2a 100644 --- a/packages/turbo-graph/src/index.ts +++ b/packages/turbo-graph/src/index.ts @@ -1,5 +1,6 @@ import { createServer } from 'node:http' import path from 'node:path' +import process from 'node:process' import { cac } from 'cac' import next from 'next' @@ -7,14 +8,14 @@ import open from 'open' import c from 'picocolors' interface Options { - o: boolean - open: boolean - p: number + open?: boolean port: number + filter?: string } const source = path.dirname(require.resolve('@yeger/turbo-graph-ui')) +const hostname = 'localhost' const defaultPort = 29312 const cli = cac('turbo-graph') @@ -22,24 +23,47 @@ const cli = cac('turbo-graph') .option('-p, --port ', 'Port of the visualizer.', { default: defaultPort, }) + .option('-f, --filter ', 'Apply a filter to the visualization.') .help() -cli.command('').action(startServer) +cli.command('[...tasks]', 'Visualize the specified tasks').action(startServer) cli.parse() -function startServer(options: Options) { - const app = next({ dev: false, dir: source }) +function startServer(tasks: string[], options: Options) { + const app = next({ dev: false, dir: source, port: options.port }) const handle = app.getRequestHandler() - createServer(handle).listen(options.port) - - const url = `http://localhost:${options.port}` - - // eslint-disable-next-line no-console - console.log(`${c.green('turbo-graph:')} Listening on ${c.cyan(url)}`) - - if (options.open) { - open(url) - } + app.prepare().then(() => { + createServer(handle) + .once('error', (err) => { + console.error(err) + process.exit(1) + }) + .listen(options.port, () => { + const url = `http://${hostname}:${options.port}` + // eslint-disable-next-line no-console + console.log(`${c.green('turbo-graph:')} Listening on ${c.cyan(url)}`) + if (options.open) { + let urlToOpen = `${url}/?` + if (tasks.length > 0) { + urlToOpen = `${urlToOpen}tasks=${tasks.join(' ')}` + } + if (options.filter) { + if (tasks.length > 0) { + urlToOpen = `${urlToOpen}&` + } + urlToOpen = `${urlToOpen}filter=${options.filter}` + } + open(urlToOpen) + } else if (tasks.length > 0 || options.filter) { + // eslint-disable-next-line no-console + console.log( + `${c.yellow('turbo-graph:')} Use ${c.cyan( + '--open', + )} to open the visualizer in the default browser with the provided tasks and filter.`, + ) + } + }) + }) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 783608a21..d3345e847 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,9 @@ importers: publint: specifier: 0.2.0 version: 0.2.0 + rimraf: + specifier: 5.0.1 + version: 5.0.1 syncpack: specifier: 10.9.3 version: 10.9.3 @@ -550,8 +553,8 @@ importers: specifier: 5.1.6 version: 5.1.6 vite: - specifier: 4.4.4 - version: 4.4.4(@types/node@18.16.19) + specifier: 4.4.9 + version: 4.4.9(@types/node@18.17.5) vite-plugin-lib: specifier: workspace:* version: link:../vite-plugin-lib @@ -567,8 +570,8 @@ importers: specifier: 6.7.14 version: 6.7.14 next: - specifier: 13.4.13 - version: 13.4.13(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.16 + version: 13.4.16(react-dom@18.2.0)(react@18.2.0) open: specifier: 8.4.2 version: 8.4.2 @@ -591,9 +594,21 @@ importers: packages/turbo-graph-ui: dependencies: - '@tanstack/react-query': - specifier: 4.32.6 - version: 4.32.6(react-dom@18.2.0)(react@18.2.0) + '@yeger/debounce': + specifier: workspace:* + version: link:../debounce + '@yeger/streams': + specifier: workspace:* + version: link:../streams + class-variance-authority: + specifier: 0.7.0 + version: 0.7.0 + clsx: + specifier: 2.0.0 + version: 2.0.0 + d3-dag: + specifier: 1.0.0 + version: 1.0.0 d3-graph-controller: specifier: workspace:* version: link:../d3-graph-controller @@ -606,12 +621,27 @@ importers: execa: specifier: 7.2.0 version: 7.2.0 + lucide-react: + specifier: ^0.268.0 + version: 0.268.0(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + reactflow: + specifier: 11.8.1 + version: 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + resumon: + specifier: workspace:* + version: link:../resumon + tailwind-merge: + specifier: ^1.14.0 + version: 1.14.0 + tailwindcss-animate: + specifier: ^1.0.6 + version: 1.0.6(tailwindcss@3.3.3) turbo: specifier: ^1.0.0 version: 1.8.8 @@ -641,8 +671,8 @@ importers: specifier: 13.4.13 version: 13.4.13(eslint@8.47.0)(typescript@5.1.6) next: - specifier: 13.4.13 - version: 13.4.13(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.16 + version: 13.4.16(react-dom@18.2.0)(react@18.2.0) postcss: specifier: 8.4.27 version: 8.4.27 @@ -2781,8 +2811,8 @@ packages: is-promise: 4.0.0 dev: true - /@next/env@13.4.13: - resolution: {integrity: sha512-fwz2QgVg08v7ZL7KmbQBLF2PubR/6zQdKBgmHEl3BCyWTEDsAQEijjw2gbFhI1tcKfLdOOJUXntz5vZ4S0Polg==} + /@next/env@13.4.16: + resolution: {integrity: sha512-pCU0sJBqdfKP9mwDadxvZd+eLz3fZrTlmmDHY12Hdpl3DD0vy8ou5HWKVfG0zZS6tqhL4wnQqRbspdY5nqa7MA==} /@next/eslint-plugin-next@13.4.13: resolution: {integrity: sha512-RpZeXlPxQ9FLeYN84XHDqRN20XxmVNclYCraLYdifRsmibtcWUWdwE/ANp2C8kgesFRsvwfsw6eOkYNl9sLJ3A==} @@ -2790,72 +2820,72 @@ packages: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.4.13: - resolution: {integrity: sha512-ZptVhHjzUuivnXMNCJ6lER33HN7lC+rZ01z+PM10Ows21NHFYMvGhi5iXkGtBDk6VmtzsbqnAjnx4Oz5um0FjA==} + /@next/swc-darwin-arm64@13.4.16: + resolution: {integrity: sha512-Rl6i1uUq0ciRa3VfEpw6GnWAJTSKo9oM2OrkGXPsm7rMxdd2FR5NkKc0C9xzFCI4+QtmBviWBdF2m3ur3Nqstw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@next/swc-darwin-x64@13.4.13: - resolution: {integrity: sha512-t9nTiWCLApw8W4G1kqJyYP7y6/7lyal3PftmRturIxAIBlZss9wrtVN8nci50StDHmIlIDxfguYIEGVr9DbFTg==} + /@next/swc-darwin-x64@13.4.16: + resolution: {integrity: sha512-o1vIKYbZORyDmTrPV1hApt9NLyWrS5vr2p5hhLGpOnkBY1cz6DAXjv8Lgan8t6X87+83F0EUDlu7klN8ieZ06A==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@next/swc-linux-arm64-gnu@13.4.13: - resolution: {integrity: sha512-xEHUqC8eqR5DHe8SOmMnDU1K3ggrJ28uIKltrQAwqFSSSmzjnN/XMocZkcVhuncuxYrpbri0iMQstRyRVdQVWg==} + /@next/swc-linux-arm64-gnu@13.4.16: + resolution: {integrity: sha512-JRyAl8lCfyTng4zoOmE6hNI2f1MFUr7JyTYCHl1RxX42H4a5LMwJhDVQ7a9tmDZ/yj+0hpBn+Aan+d6lA3v0UQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-arm64-musl@13.4.13: - resolution: {integrity: sha512-sNf3MnLAm8rquSSAoeD9nVcdaDeRYOeey4stOWOyWIgbBDtP+C93amSgH/LPTDoUV7gNiU6f+ghepTjTjRgIUQ==} + /@next/swc-linux-arm64-musl@13.4.16: + resolution: {integrity: sha512-9gqVqNzUMWbUDgDiND18xoUqhwSm2gmksqXgCU0qaOKt6oAjWz8cWYjgpPVD0WICKFylEY/gvPEP1fMZDVFZ/g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-gnu@13.4.13: - resolution: {integrity: sha512-WhcRaJJSHyx9OWmKjjz+OWHumiPZWRqmM/09Bt7Up4UqUJFFhGExeztR4trtv3rflvULatu9IH/nTV8fUUgaMA==} + /@next/swc-linux-x64-gnu@13.4.16: + resolution: {integrity: sha512-KcQGwchAKmZVPa8i5PLTxvTs1/rcFnSltfpTm803Tr/BtBV3AxCkHLfhtoyVtVzx/kl/oue8oS+DSmbepQKwhw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-musl@13.4.13: - resolution: {integrity: sha512-+Y4LLhOWWZQIDKVwr2R17lq2KSN0F1c30QVgGIWfnjjHpH8nrIWHEndhqYU+iFuW8It78CiJjQKTw4f51HD7jA==} + /@next/swc-linux-x64-musl@13.4.16: + resolution: {integrity: sha512-2RbMZNxYnJmW8EPHVBsGZPq5zqWAyBOc/YFxq/jIQ/Yn3RMFZ1dZVCjtIcsiaKmgh7mjA/W0ApbumutHNxRqqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-win32-arm64-msvc@13.4.13: - resolution: {integrity: sha512-rWurdOR20uxjfqd1X9vDAgv0Jb26KjyL8akF9CBeFqX8rVaBAnW/Wf6A2gYEwyYY4Bai3T7p1kro6DFrsvBAAw==} + /@next/swc-win32-arm64-msvc@13.4.16: + resolution: {integrity: sha512-thDcGonELN7edUKzjzlHrdoKkm7y8IAdItQpRvvMxNUXa4d9r0ElofhTZj5emR7AiXft17hpen+QAkcWpqG7Jg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-ia32-msvc@13.4.13: - resolution: {integrity: sha512-E8bSPwRuY5ibJ3CzLQmJEt8qaWrPYuUTwnrwygPUEWoLzD5YRx9SD37oXRdU81TgGwDzCxpl7z5Nqlfk50xAog==} + /@next/swc-win32-ia32-msvc@13.4.16: + resolution: {integrity: sha512-f7SE1Mo4JAchUWl0LQsbtySR9xCa+x55C0taetjUApKtcLR3AgAjASrrP+oE1inmLmw573qRnE1eZN8YJfEBQw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-x64-msvc@13.4.13: - resolution: {integrity: sha512-4KlyC6jWRubPnppgfYsNTPeWfGCxtWLh5vaOAW/kdzAk9widqho8Qb5S4K2vHmal1tsURi7Onk2MMCV1phvyqA==} + /@next/swc-win32-x64-msvc@13.4.16: + resolution: {integrity: sha512-WamDZm1M/OEM4QLce3lOmD1XdLEl37zYZwlmOLhmF7qYJ2G6oYm9+ejZVv+LakQIsIuXhSpVlOvrxIAHqwRkPQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2930,7 +2960,7 @@ packages: scule: 1.0.0 semver: 7.5.4 unctx: 2.3.1 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -2956,7 +2986,7 @@ packages: scule: 1.0.0 semver: 7.5.4 unctx: 2.3.1 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -2982,7 +3012,7 @@ packages: scule: 1.0.0 semver: 7.5.4 unctx: 2.3.1 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -3004,7 +3034,7 @@ packages: scule: 1.0.0 std-env: 3.3.3 ufo: 1.2.0 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -3022,7 +3052,7 @@ packages: postcss-import-resolver: 2.0.0 std-env: 3.3.3 ufo: 1.2.0 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -3040,7 +3070,7 @@ packages: postcss-import-resolver: 2.0.0 std-env: 3.3.3 ufo: 1.2.0 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -3058,7 +3088,7 @@ packages: postcss-import-resolver: 2.0.0 std-env: 3.3.3 ufo: 1.2.0 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14 untyped: 1.3.2 transitivePeerDependencies: - rollup @@ -3105,7 +3135,7 @@ packages: vue: ^3.3.4 dependencies: '@nuxt/kit': 3.5.3 - '@rollup/plugin-replace': 5.0.2(rollup@3.26.3) + '@rollup/plugin-replace': 5.0.2 '@vitejs/plugin-vue': 4.2.3(vite@4.3.9)(vue@3.3.4) '@vitejs/plugin-vue-jsx': 3.0.1(vite@4.3.9)(vue@3.3.4) autoprefixer: 10.4.14(postcss@8.4.27) @@ -3130,7 +3160,7 @@ packages: postcss: 8.4.27 postcss-import: 15.1.0(postcss@8.4.27) postcss-url: 10.1.3(postcss@8.4.27) - rollup-plugin-visualizer: 5.9.2(rollup@3.26.3) + rollup-plugin-visualizer: 5.9.2(rollup@3.28.0) std-env: 3.3.3 strip-literal: 1.0.1 ufo: 1.2.0 @@ -3208,6 +3238,114 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true + /@reactflow/background@11.2.6(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-SoBArxNk/NygB6ztCR2RWVcx7yRh+zuSKh37bLbW+hdFcTx6ZgC4vs5+HX2xGY9ZIyR9ipg4Z3+l11ubyzr/lw==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + classcat: 5.0.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@reactflow/controls@11.1.17(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-iMMuTwF5QEqlgqKr+3wg3YS0wyEQOX2DV1AQJUaZ9WSW17cjH/pH1B7iNAS1giWyyvpYvd1HiXG2zpJIS/NQDg==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + classcat: 5.0.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@reactflow/core@11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Ob/21U3Wnugq10zbBESwUxH6NMBoJGvBmJdLJB8ux/w4mzGUMxTR78gFXAsPGsuO3J/pf9MVMPrhTsPA0rApCw==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@types/d3': 7.4.0 + '@types/d3-drag': 3.0.2 + '@types/d3-selection': 3.0.5 + '@types/d3-zoom': 3.0.3 + classcat: 5.0.4 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@reactflow/minimap@11.6.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-158m6x0f7es5cmmkfhZYVNI7Yc5gxo+9aHqNDFwu9SR5HeyOU5ezJ6CDsSdHuM6xUNq3kXY1f0Ds6YxvyfRmGg==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@types/d3-selection': 3.0.5 + '@types/d3-zoom': 3.0.3 + classcat: 5.0.4 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@reactflow/node-resizer@2.1.3(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wwEcftxG4BBQGpIfPdsSyDTM5XxwXBkihcoVJRY92t71qNi9CzSuX1xoFO2jvP8thwk8MiWdppVuCKs+1QVAXA==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + classcat: 5.0.4 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@reactflow/node-toolbar@1.2.5(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-VrnlzumrnTYGhX5CDylSuPgONexTbI5rnnI+NJWeW+9LXm1mkn3A9DUbIn3jWKcBYdzKW8GPyuPFzaCw0EyVZw==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + classcat: 5.0.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + zustand: 4.4.1(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + /@rollup/plugin-alias@5.0.0(rollup@3.26.3): resolution: {integrity: sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==} engines: {node: '>=14.0.0'} @@ -3221,7 +3359,20 @@ packages: slash: 4.0.0 dev: true - /@rollup/plugin-commonjs@25.0.2(rollup@3.26.3): + /@rollup/plugin-alias@5.0.0(rollup@3.28.0): + resolution: {integrity: sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + rollup: 3.28.0 + slash: 4.0.0 + dev: true + + /@rollup/plugin-commonjs@25.0.2(rollup@3.28.0): resolution: {integrity: sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3230,13 +3381,13 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.26.3) + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) commondir: 1.0.1 estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 magic-string: 0.27.0 - rollup: 3.26.3 + rollup: 3.28.0 dev: true /@rollup/plugin-commonjs@25.0.3(rollup@3.26.3): @@ -3257,7 +3408,7 @@ packages: rollup: 3.26.3 dev: true - /@rollup/plugin-inject@5.0.3(rollup@3.26.3): + /@rollup/plugin-inject@5.0.3(rollup@3.28.0): resolution: {integrity: sha512-411QlbL+z2yXpRWFXSmw/teQRMkXcAAC8aYTemc15gwJRpvEVDQwoe+N/HTFD8RFG8+88Bme9DK2V9CVm7hJdA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3266,10 +3417,10 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.26.3) + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) estree-walker: 2.0.2 magic-string: 0.27.0 - rollup: 3.26.3 + rollup: 3.28.0 dev: true /@rollup/plugin-json@6.0.0(rollup@3.26.3): @@ -3285,6 +3436,19 @@ packages: rollup: 3.26.3 dev: true + /@rollup/plugin-json@6.0.0(rollup@3.28.0): + resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) + rollup: 3.28.0 + dev: true + /@rollup/plugin-node-resolve@15.1.0(rollup@3.26.3): resolution: {integrity: sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==} engines: {node: '>=14.0.0'} @@ -3303,6 +3467,37 @@ packages: rollup: 3.26.3 dev: true + /@rollup/plugin-node-resolve@15.1.0(rollup@3.28.0): + resolution: {integrity: sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.3 + rollup: 3.28.0 + dev: true + + /@rollup/plugin-replace@5.0.2: + resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@3.26.3) + magic-string: 0.27.0 + dev: true + /@rollup/plugin-replace@5.0.2(rollup@3.26.3): resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} engines: {node: '>=14.0.0'} @@ -3317,7 +3512,21 @@ packages: rollup: 3.26.3 dev: true - /@rollup/plugin-terser@0.4.3(rollup@3.26.3): + /@rollup/plugin-replace@5.0.2(rollup@3.28.0): + resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) + magic-string: 0.27.0 + rollup: 3.28.0 + dev: true + + /@rollup/plugin-terser@0.4.3(rollup@3.28.0): resolution: {integrity: sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3326,13 +3535,13 @@ packages: rollup: optional: true dependencies: - rollup: 3.26.3 + rollup: 3.28.0 serialize-javascript: 6.0.1 smob: 1.4.0 terser: 5.18.0 dev: true - /@rollup/plugin-wasm@6.1.3(rollup@3.26.3): + /@rollup/plugin-wasm@6.1.3(rollup@3.28.0): resolution: {integrity: sha512-7ItTTeyauE6lwdDtQWceEHZ9+txbi4RRy0mYPFn9BW7rD7YdgBDu7HTHsLtHrRzJc313RM/1m6GKgV3np/aEaw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -3341,7 +3550,7 @@ packages: rollup: optional: true dependencies: - rollup: 3.26.3 + rollup: 3.28.0 dev: true /@rollup/pluginutils@4.2.1: @@ -3366,6 +3575,21 @@ packages: picomatch: 2.3.1 rollup: 3.26.3 + /@rollup/pluginutils@5.0.2(rollup@3.28.0): + resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.0 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 3.28.0 + dev: true + /@rushstack/eslint-patch@1.2.0: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} dev: true @@ -3417,28 +3641,6 @@ packages: dependencies: tslib: 2.5.0 - /@tanstack/query-core@4.32.6: - resolution: {integrity: sha512-YVB+mVWENQwPyv+40qO7flMgKZ0uI41Ph7qXC2Zf1ft5AIGfnXnMZyifB2ghhZ27u+5wm5mlzO4Y6lwwadzxCA==} - dev: false - - /@tanstack/react-query@4.32.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-AITu/IKJJJXsHHeXNBy5bclu12t08usMCY0vFC2dh9SP/w6JAk5U9GwfjOIPj3p+ATADZvxQPe8UiCtMLNeQbg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@tanstack/query-core': 4.32.6 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - /@tootallnate/once@2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -3511,50 +3713,175 @@ packages: resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} dev: true + /@types/d3-array@3.0.5: + resolution: {integrity: sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==} + dev: false + + /@types/d3-axis@3.0.2: + resolution: {integrity: sha512-uGC7DBh0TZrU/LY43Fd8Qr+2ja1FKmH07q2FoZFHo1eYl8aj87GhfVoY1saJVJiq24rp1+wpI6BvQJMKgQm8oA==} + dependencies: + '@types/d3-selection': 3.0.5 + dev: false + + /@types/d3-brush@3.0.2: + resolution: {integrity: sha512-2TEm8KzUG3N7z0TrSKPmbxByBx54M+S9lHoP2J55QuLU0VSQ9mE96EJSAOVNEqd1bbynMjeTS9VHmz8/bSw8rA==} + dependencies: + '@types/d3-selection': 3.0.5 + dev: false + + /@types/d3-chord@3.0.2: + resolution: {integrity: sha512-abT/iLHD3sGZwqMTX1TYCMEulr+wBd0SzyOQnjYNLp7sngdOHYtNkMRI5v3w5thoN+BWtlHVDx2Osvq6fxhZWw==} + dev: false + /@types/d3-color@3.1.0: resolution: {integrity: sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==} - dev: true + + /@types/d3-contour@3.0.2: + resolution: {integrity: sha512-k6/bGDoAGJZnZWaKzeB+9glgXCYGvh6YlluxzBREiVo8f/X2vpTEdgPy9DN7Z2i42PZOZ4JDhVdlTSTSkLDPlQ==} + dependencies: + '@types/d3-array': 3.0.5 + '@types/geojson': 7946.0.10 + dev: false + + /@types/d3-delaunay@6.0.1: + resolution: {integrity: sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==} + dev: false + + /@types/d3-dispatch@3.0.2: + resolution: {integrity: sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==} + dev: false /@types/d3-drag@3.0.2: resolution: {integrity: sha512-qmODKEDvyKWVHcWWCOVcuVcOwikLVsyc4q4EBJMREsoQnR2Qoc2cZQUyFUPgO9q4S3qdSqJKBsuefv+h0Qy+tw==} dependencies: '@types/d3-selection': 3.0.5 - dev: true + + /@types/d3-dsv@3.0.1: + resolution: {integrity: sha512-76pBHCMTvPLt44wFOieouXcGXWOF0AJCceUvaFkxSZEu4VDUdv93JfpMa6VGNFs01FHfuP4a5Ou68eRG1KBfTw==} + dev: false + + /@types/d3-ease@3.0.0: + resolution: {integrity: sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==} + dev: false + + /@types/d3-fetch@3.0.2: + resolution: {integrity: sha512-gllwYWozWfbep16N9fByNBDTkJW/SyhH6SGRlXloR7WdtAaBui4plTP+gbUgiEot7vGw/ZZop1yDZlgXXSuzjA==} + dependencies: + '@types/d3-dsv': 3.0.1 + dev: false /@types/d3-force@3.0.4: resolution: {integrity: sha512-q7xbVLrWcXvSBBEoadowIUJ7sRpS1yvgMWnzHJggFy5cUZBq2HZL5k/pBSm0GdYWS1vs5/EDwMjSKF55PDY4Aw==} - dev: true + + /@types/d3-format@3.0.1: + resolution: {integrity: sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==} + dev: false + + /@types/d3-geo@3.0.3: + resolution: {integrity: sha512-bK9uZJS3vuDCNeeXQ4z3u0E7OeJZXjUgzFdSOtNtMCJCLvDtWDwfpRVWlyt3y8EvRzI0ccOu9xlMVirawolSCw==} + dependencies: + '@types/geojson': 7946.0.10 + dev: false + + /@types/d3-hierarchy@3.1.2: + resolution: {integrity: sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==} + dev: false /@types/d3-interpolate@3.0.1: resolution: {integrity: sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==} dependencies: '@types/d3-color': 3.1.0 - dev: true + + /@types/d3-path@3.0.0: + resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==} + dev: false + + /@types/d3-polygon@3.0.0: + resolution: {integrity: sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==} + dev: false + + /@types/d3-quadtree@3.0.2: + resolution: {integrity: sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==} + dev: false + + /@types/d3-random@3.0.1: + resolution: {integrity: sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==} + dev: false /@types/d3-scale-chromatic@3.0.0: resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==} - dev: true /@types/d3-scale@4.0.3: resolution: {integrity: sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==} dependencies: '@types/d3-time': 3.0.0 - dev: true /@types/d3-selection@3.0.5: resolution: {integrity: sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==} - dev: true + + /@types/d3-shape@3.1.1: + resolution: {integrity: sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==} + dependencies: + '@types/d3-path': 3.0.0 + dev: false + + /@types/d3-time-format@4.0.0: + resolution: {integrity: sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==} + dev: false /@types/d3-time@3.0.0: resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} - dev: true + + /@types/d3-timer@3.0.0: + resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==} + dev: false + + /@types/d3-transition@3.0.3: + resolution: {integrity: sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==} + dependencies: + '@types/d3-selection': 3.0.5 + dev: false /@types/d3-zoom@3.0.3: resolution: {integrity: sha512-OWk1yYIIWcZ07+igN6BeoG6rqhnJ/pYe+R1qWFM2DtW49zsoSjgb9G5xB0ZXA8hh2jAzey1XuRmMSoXdKw8MDA==} dependencies: '@types/d3-interpolate': 3.0.1 '@types/d3-selection': 3.0.5 - dev: true + + /@types/d3@7.4.0: + resolution: {integrity: sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==} + dependencies: + '@types/d3-array': 3.0.5 + '@types/d3-axis': 3.0.2 + '@types/d3-brush': 3.0.2 + '@types/d3-chord': 3.0.2 + '@types/d3-color': 3.1.0 + '@types/d3-contour': 3.0.2 + '@types/d3-delaunay': 6.0.1 + '@types/d3-dispatch': 3.0.2 + '@types/d3-drag': 3.0.2 + '@types/d3-dsv': 3.0.1 + '@types/d3-ease': 3.0.0 + '@types/d3-fetch': 3.0.2 + '@types/d3-force': 3.0.4 + '@types/d3-format': 3.0.1 + '@types/d3-geo': 3.0.3 + '@types/d3-hierarchy': 3.1.2 + '@types/d3-interpolate': 3.0.1 + '@types/d3-path': 3.0.0 + '@types/d3-polygon': 3.0.0 + '@types/d3-quadtree': 3.0.2 + '@types/d3-random': 3.0.1 + '@types/d3-scale': 4.0.3 + '@types/d3-scale-chromatic': 3.0.0 + '@types/d3-selection': 3.0.5 + '@types/d3-shape': 3.1.1 + '@types/d3-time': 3.0.0 + '@types/d3-time-format': 4.0.0 + '@types/d3-timer': 3.0.0 + '@types/d3-transition': 3.0.3 + '@types/d3-zoom': 3.0.3 + dev: false /@types/eslint-scope@3.7.4: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} @@ -3576,6 +3903,10 @@ packages: /@types/estree@1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + /@types/geojson@7946.0.10: + resolution: {integrity: sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==} + dev: false + /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: @@ -3653,7 +3984,6 @@ packages: /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - dev: true /@types/react-dom@18.2.7: resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} @@ -3667,7 +3997,6 @@ packages: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.3 csstype: 3.1.2 - dev: true /@types/resize-observer-browser@0.1.7: resolution: {integrity: sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==} @@ -3679,7 +4008,6 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - dev: true /@types/semver@7.5.0: resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} @@ -5973,6 +6301,16 @@ packages: consola: 3.2.3 dev: true + /class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + dependencies: + clsx: 2.0.0 + dev: false + + /classcat@5.0.4: + resolution: {integrity: sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==} + dev: false + /clean-css@4.2.4: resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} engines: {node: '>= 4.0'} @@ -6073,6 +6411,11 @@ packages: engines: {node: '>=0.8'} dev: true + /clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + dev: false + /cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} @@ -6745,11 +7088,27 @@ packages: internmap: 2.0.3 dev: false + /d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + dependencies: + internmap: 2.0.3 + dev: false + /d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} engines: {node: '>=12'} dev: false + /d3-dag@1.0.0: + resolution: {integrity: sha512-OJaaYqd9tSu0TB8QWfAVO74btk7xWH4g8pJissMnSC12JnUU87HDBSGJ6ebriPvLS9Rt8PFGgkqXdg/fYdPovQ==} + dependencies: + d3-array: 3.2.4 + javascript-lp-solver: 0.4.24 + quadprog: 1.6.1 + stringify-object: 5.0.0 + dev: false + /d3-dispatch@3.0.1: resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} engines: {node: '>=12'} @@ -8367,7 +8726,7 @@ packages: optionator: 0.8.3 progress: 2.0.3 regexpp: 2.0.1 - semver: 6.3.0 + semver: 6.3.1 strip-ansi: 5.2.0 strip-json-comments: 3.1.1 table: 5.4.6 @@ -8996,6 +9355,11 @@ packages: has-proto: 1.0.1 has-symbols: 1.0.3 + /get-own-enumerable-keys@1.0.0: + resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} + engines: {node: '>=14.16'} + dev: false + /get-port-please@3.0.1: resolution: {integrity: sha512-R5pcVO8Z1+pVDu8Ml3xaJCEkBiiy1VQN9za0YqH8GIi1nIqD4IzQhzY6dDzMRtdS1lyiGlucRzm8IN8wtLIXng==} dev: true @@ -9099,7 +9463,7 @@ packages: dependencies: foreground-child: 3.1.1 jackspeak: 2.2.0 - minimatch: 9.0.2 + minimatch: 9.0.3 minipass: 5.0.0 path-scurry: 1.7.0 dev: true @@ -9940,6 +10304,11 @@ packages: engines: {node: '>=8'} dev: true + /is-obj@3.0.0: + resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} + engines: {node: '>=12'} + dev: false + /is-path-cwd@2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} engines: {node: '>=6'} @@ -9985,6 +10354,11 @@ packages: call-bind: 1.0.2 has-tostringtag: 1.0.0 + /is-regexp@3.1.0: + resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} + engines: {node: '>=12'} + dev: false + /is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} @@ -10155,6 +10529,10 @@ packages: '@pkgjs/parseargs': 0.11.0 dev: true + /javascript-lp-solver@0.4.24: + resolution: {integrity: sha512-5edoDKnMrt/u3M6GnZKDDIPxOyFOg+WrwDv8mjNiMC2DePhy2H9/FFQgf4ggywaXT1utvkxusJcjQUER72cZmA==} + dev: false + /jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -10702,6 +11080,14 @@ packages: engines: {node: 14 || >=16.14} dev: true + /lucide-react@0.268.0(react@18.2.0): + resolution: {integrity: sha512-XP/xY3ASJAViqNqVnDRcEfdxfRB7uNST8sqTLwZhL983ikmHMQ7qQak7ZxrnXOVhB3QDBawdr3ANq0P+iWHP/g==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /magic-string-ast@0.1.2: resolution: {integrity: sha512-P53AZrzq7hclCU6HWj88xNZHmP15DKjMmK/vBytO1qnpYP3ul4IEZlyCE0aU3JRnmgWmZPmoTKj4Bls7v0pMyA==} engines: {node: '>=14.19.0'} @@ -11155,8 +11541,8 @@ packages: engines: {node: '>= 0.4.0'} dev: true - /next@13.4.13(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-A3YVbVDNeXLhWsZ8Nf6IkxmNlmTNz0yVg186NJ97tGZqPDdPzTrHotJ+A1cuJm2XfuWPrKOUZILl5iBQkIf8Jw==} + /next@13.4.16(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-1xaA/5DrfpPu0eV31Iro7JfPeqO8uxQWb1zYNTe+KDKdzqkAGapLcDYHMLNKXKB7lHjZ7LfKUOf9dyuzcibrhA==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: @@ -11170,7 +11556,7 @@ packages: sass: optional: true dependencies: - '@next/env': 13.4.13 + '@next/env': 13.4.16 '@swc/helpers': 0.5.1 busboy: 1.6.0 caniuse-lite: 1.0.30001477 @@ -11181,15 +11567,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.13 - '@next/swc-darwin-x64': 13.4.13 - '@next/swc-linux-arm64-gnu': 13.4.13 - '@next/swc-linux-arm64-musl': 13.4.13 - '@next/swc-linux-x64-gnu': 13.4.13 - '@next/swc-linux-x64-musl': 13.4.13 - '@next/swc-win32-arm64-msvc': 13.4.13 - '@next/swc-win32-ia32-msvc': 13.4.13 - '@next/swc-win32-x64-msvc': 13.4.13 + '@next/swc-darwin-arm64': 13.4.16 + '@next/swc-darwin-x64': 13.4.16 + '@next/swc-linux-arm64-gnu': 13.4.16 + '@next/swc-linux-arm64-musl': 13.4.16 + '@next/swc-linux-x64-gnu': 13.4.16 + '@next/swc-linux-x64-musl': 13.4.16 + '@next/swc-win32-arm64-msvc': 13.4.16 + '@next/swc-win32-ia32-msvc': 13.4.16 + '@next/swc-win32-x64-msvc': 13.4.16 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -11205,15 +11591,15 @@ packages: dependencies: '@cloudflare/kv-asset-handler': 0.3.0 '@netlify/functions': 1.6.0 - '@rollup/plugin-alias': 5.0.0(rollup@3.26.3) - '@rollup/plugin-commonjs': 25.0.2(rollup@3.26.3) - '@rollup/plugin-inject': 5.0.3(rollup@3.26.3) - '@rollup/plugin-json': 6.0.0(rollup@3.26.3) - '@rollup/plugin-node-resolve': 15.1.0(rollup@3.26.3) - '@rollup/plugin-replace': 5.0.2(rollup@3.26.3) - '@rollup/plugin-terser': 0.4.3(rollup@3.26.3) - '@rollup/plugin-wasm': 6.1.3(rollup@3.26.3) - '@rollup/pluginutils': 5.0.2(rollup@3.26.3) + '@rollup/plugin-alias': 5.0.0(rollup@3.28.0) + '@rollup/plugin-commonjs': 25.0.2(rollup@3.28.0) + '@rollup/plugin-inject': 5.0.3(rollup@3.28.0) + '@rollup/plugin-json': 6.0.0(rollup@3.28.0) + '@rollup/plugin-node-resolve': 15.1.0(rollup@3.28.0) + '@rollup/plugin-replace': 5.0.2(rollup@3.28.0) + '@rollup/plugin-terser': 0.4.3(rollup@3.28.0) + '@rollup/plugin-wasm': 6.1.3(rollup@3.28.0) + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) '@types/http-proxy': 1.17.11 '@vercel/nft': 0.22.6 archiver: 5.3.1 @@ -11254,8 +11640,8 @@ packages: pkg-types: 1.0.3 pretty-bytes: 6.1.1 radix3: 1.0.1 - rollup: 3.26.3 - rollup-plugin-visualizer: 5.9.2(rollup@3.26.3) + rollup: 3.28.0 + rollup-plugin-visualizer: 5.9.2(rollup@3.28.0) scule: 1.0.0 semver: 7.5.4 serve-placeholder: 2.0.1 @@ -11265,7 +11651,7 @@ packages: ufo: 1.2.0 uncrypto: 0.1.3 unenv: 1.5.1 - unimport: 3.0.14(rollup@3.26.3) + unimport: 3.0.14(rollup@3.28.0) unstorage: 1.7.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -12786,6 +13172,11 @@ packages: resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} dev: true + /quadprog@1.6.1: + resolution: {integrity: sha512-fN5Jkcjlln/b3pJkseDKREf89JkKIyu6cKIVXisgL6ocKPQ0yTp9n6NZUAq3otEPPw78WZMG9K0o9WsfKyMWJw==} + engines: {node: '>=8.x'} + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: true @@ -12854,6 +13245,25 @@ packages: dependencies: loose-envify: 1.4.0 + /reactflow@11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OuhsSCiefrCAUZqjuYJNVhhUpLrNQNzBz1rORCMXYO2j7y0FQ02oZLoMT/5mhCmNGMqR7BZwwvOMCSZM7wAn5A==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@reactflow/background': 11.2.6(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/controls': 11.1.17(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.8.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/minimap': 11.6.1(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-resizer': 2.1.3(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/node-toolbar': 1.2.5(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + /read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: @@ -13644,7 +14054,7 @@ packages: '@babel/code-frame': 7.21.4 dev: true - /rollup-plugin-visualizer@5.9.2(rollup@3.26.3): + /rollup-plugin-visualizer@5.9.2(rollup@3.28.0): resolution: {integrity: sha512-waHktD5mlWrYFrhOLbti4YgQCn1uR24nYsNuXxg7LkPH8KdTXVWR9DNY1WU0QqokyMixVXJS4J04HNrVTMP01A==} engines: {node: '>=14'} hasBin: true @@ -13656,7 +14066,7 @@ packages: dependencies: open: 8.4.2 picomatch: 2.3.1 - rollup: 3.26.3 + rollup: 3.28.0 source-map: 0.7.4 yargs: 17.7.2 dev: true @@ -14260,6 +14670,15 @@ packages: is-hexadecimal: 1.0.4 dev: false + /stringify-object@5.0.0: + resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} + engines: {node: '>=14.16'} + dependencies: + get-own-enumerable-keys: 1.0.0 + is-obj: 3.0.0 + is-regexp: 3.1.0 + dev: false + /strip-ansi@5.2.0: resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} engines: {node: '>=6'} @@ -14472,6 +14891,18 @@ packages: string-width: 3.1.0 dev: false + /tailwind-merge@1.14.0: + resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==} + dev: false + + /tailwindcss-animate@1.0.6(tailwindcss@3.3.3): + resolution: {integrity: sha512-4WigSGMvbl3gCCact62ZvOngA+PRqhAn7si3TQ3/ZuPuQZcIEtVap+ENSXbzWhpojKB8CpvnIsrwBu8/RnHtuw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + tailwindcss: 3.3.3(ts-node@10.9.1) + dev: false + /tailwindcss@3.3.3(ts-node@10.9.1): resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} engines: {node: '>=14.0.0'} @@ -15223,7 +15654,7 @@ packages: vfile: 4.2.1 dev: false - /unimport@3.0.14(rollup@3.26.3): + /unimport@3.0.14: resolution: {integrity: sha512-67Rh/sGpEuVqdHWkXaZ6NOq+I7sKt86o+DUtKeGB6dh4Hk1A8AQrzyVGg2+LaVEYotStH7HwvV9YSaRjyT7Uqg==} dependencies: '@rollup/pluginutils': 5.0.2(rollup@3.26.3) @@ -15241,6 +15672,24 @@ packages: - rollup dev: true + /unimport@3.0.14(rollup@3.28.0): + resolution: {integrity: sha512-67Rh/sGpEuVqdHWkXaZ6NOq+I7sKt86o+DUtKeGB6dh4Hk1A8AQrzyVGg2+LaVEYotStH7HwvV9YSaRjyT7Uqg==} + dependencies: + '@rollup/pluginutils': 5.0.2(rollup@3.28.0) + escape-string-regexp: 5.0.0 + fast-glob: 3.3.0 + local-pkg: 0.4.3 + magic-string: 0.30.1 + mlly: 1.4.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + scule: 1.0.0 + strip-literal: 1.0.1 + unplugin: 1.3.2 + transitivePeerDependencies: + - rollup + dev: true + /unimport@3.0.6: resolution: {integrity: sha512-GYxGJ1Bri1oqx8VFDjdgooGzeK7jBk3bvhXmamTIpu3nONOcUMGwZbX7X0L5RA7OWMXpR4vzpSQP7pXUzJg1/Q==} dependencies: @@ -15826,7 +16275,7 @@ packages: '@types/node': 18.17.5 esbuild: 0.17.19 postcss: 8.4.27 - rollup: 3.26.3 + rollup: 3.28.0 optionalDependencies: fsevents: 2.3.2 dev: true @@ -16573,3 +17022,23 @@ packages: /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + + /zustand@4.4.1(@types/react@18.2.20)(react@18.2.0): + resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.20 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false