[])[0]).toEqual({ name: 'Alice', age: '30' });
+ });
+
+ it('does not mistake comma-prose for CSV', () => {
+ expect(detectFormat('Hi there,\nthanks a lot, really, for everything you did.').renderer).toBe('markdown');
+ });
+
+ it('does not treat a bare scalar as JSON', () => {
+ expect(detectFormat('42').renderer).toBe('markdown');
+ expect(detectFormat('"hello"').renderer).toBe('markdown');
+ });
+});
diff --git a/apps/ai-studio/src/utils/detect-format.ts b/apps/ai-studio/src/utils/detect-format.ts
new file mode 100644
index 000000000..5eadb37c4
--- /dev/null
+++ b/apps/ai-studio/src/utils/detect-format.ts
@@ -0,0 +1,157 @@
+// Heuristic format detection for the Visualize node's `auto` mode. Maps a raw
+// output string to the renderer that fits it best. Conservative by design: only
+// picks `chart`/`diagram` on an unambiguous signal, and falls back to `markdown`
+// (react-markdown renders plain prose fine too) rather than raw `text`. `text`
+// () is reachable via explicit override only.
+
+export type VisualizeRenderer = 'markdown' | 'text' | 'json' | 'table' | 'stat-cards' | 'chart' | 'diagram';
+
+type DetectResult = {
+ renderer: VisualizeRenderer;
+ // Parsed payload for structured formats (json/table/chart), so renderers do not re-parse.
+ data?: unknown;
+ // True when the data could also be charted (drives the "try as chart" suggestion).
+ chartable?: boolean;
+};
+
+// A fenced ```mermaid block, or a first line that is unambiguously a mermaid
+// declaration. Deliberately strict: flowchart/graph need a direction, and
+// prose-like bare words ("pie", "graph", "timeline", "journey") are NOT matched,
+// so plain prose starting with such a word is not mis-detected as a diagram.
+const MERMAID_FENCE = /^```mermaid\s*\n?([\s\S]*?)```$/;
+const MERMAID_FIRST_LINE =
+ /^(?:sequenceDiagram|classDiagram|stateDiagram(?:-v2)?|erDiagram|gantt|gitGraph|mindmap|quadrantChart|requirementDiagram)\b|^(?:flowchart|graph)\s+(?:TB|TD|BT|RL|LR)\b/;
+
+const LABEL_KEYS = new Set(['label', 'name', 'category', 'x', 'key', 'date', 'month', 'day']);
+const VALUE_KEYS = new Set(['value', 'count', 'y', 'amount', 'total', 'qty', 'quantity', 'score']);
+const CHART_TYPES = new Set(['bar', 'line', 'pie', 'area', 'donut']);
+
+function isScalar(value: unknown): boolean {
+ return value === null || ['string', 'number', 'boolean'].includes(typeof value);
+}
+
+function isPlainObject(value: unknown): value is Record {
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
+}
+
+function looksLikeChartArray(array: unknown[]): boolean {
+ if (array.length === 0) {
+ return false;
+ }
+ return array.every((item) => {
+ if (!isPlainObject(item)) {
+ return false;
+ }
+ const keys = Object.keys(item).map((k) => k.toLowerCase());
+ const hasLabel = keys.some((k) => LABEL_KEYS.has(k));
+ const hasNumericValue = Object.entries(item).some(
+ ([k, v]) => VALUE_KEYS.has(k.toLowerCase()) && typeof v === 'number',
+ );
+ return hasLabel && hasNumericValue;
+ });
+}
+
+function hasNumericColumn(rows: Record[]): boolean {
+ if (rows.length === 0) {
+ return false;
+ }
+ return Object.keys(rows[0]).some((key) => rows.every((row) => typeof row[key] === 'number'));
+}
+
+function detectJson(parsed: unknown): DetectResult | null {
+ // Explicit chart spec envelope: { type: 'bar'|'line'|..., data: [...] }
+ if (
+ isPlainObject(parsed) &&
+ typeof parsed['type'] === 'string' &&
+ Array.isArray(parsed['data']) &&
+ CHART_TYPES.has(parsed['type'].toLowerCase())
+ ) {
+ return { renderer: 'chart', data: parsed };
+ }
+
+ if (Array.isArray(parsed)) {
+ if (looksLikeChartArray(parsed)) {
+ return { renderer: 'chart', data: parsed, chartable: true };
+ }
+ if (parsed.length > 0 && parsed.every(isPlainObject)) {
+ return { renderer: 'table', data: parsed, chartable: hasNumericColumn(parsed as Record[]) };
+ }
+ return {
+ renderer: 'table',
+ data: parsed,
+ chartable: parsed.length > 0 && parsed.every((v) => typeof v === 'number'),
+ };
+ }
+
+ if (isPlainObject(parsed)) {
+ return Object.values(parsed).every(isScalar)
+ ? { renderer: 'stat-cards', data: parsed }
+ : { renderer: 'json', data: parsed };
+ }
+
+ return null; // scalar JSON (number/string/bool) is not "structured"
+}
+
+function parseCsv(text: string): Record[] | null {
+ const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
+ if (lines.length < 2) {
+ return null;
+ }
+ const delimiter = lines[0].includes('\t') ? '\t' : ',';
+ const counts = lines.map((line) => line.split(delimiter).length);
+ // Strong guard against prose: every line must have the same column count (>= 2).
+ if (counts[0] < 2 || !counts.every((c) => c === counts[0])) {
+ return null;
+ }
+ const headers = lines[0].split(delimiter).map((h) => h.trim());
+ // Headers should look like headers, not sentences.
+ if (headers.some((h) => h.length === 0 || h.length > 30)) {
+ return null;
+ }
+ return lines.slice(1).map((line) => {
+ const cells = line.split(delimiter);
+ const row: Record = {};
+ for (const [index, header] of headers.entries()) {
+ row[header] = (cells[index] ?? '').trim();
+ }
+ return row;
+ });
+}
+
+export function detectFormat(input: string): DetectResult {
+ const text = (input ?? '').trim();
+ if (!text) {
+ return { renderer: 'text' };
+ }
+
+ // 1. Mermaid diagram: a fenced ```mermaid block, or a clearly-declared first line.
+ const fence = MERMAID_FENCE.exec(text);
+ if (fence) {
+ return { renderer: 'diagram', data: fence[1].trim() };
+ }
+ const firstLine = text.split(/\r?\n/)[0].trim();
+ if (MERMAID_FIRST_LINE.test(firstLine)) {
+ return { renderer: 'diagram', data: text };
+ }
+
+ // 2. JSON (gate on { or [ so bare scalars do not register as JSON).
+ if (text.startsWith('{') || text.startsWith('[')) {
+ try {
+ const result = detectJson(JSON.parse(text));
+ if (result) {
+ return result;
+ }
+ } catch {
+ // not valid JSON — fall through
+ }
+ }
+
+ // 3. CSV / TSV.
+ const csv = parseCsv(text);
+ if (csv) {
+ return { renderer: 'table', data: csv };
+ }
+
+ // 4. Fallback: markdown (handles both real markdown and plain prose well).
+ return { renderer: 'markdown' };
+}
diff --git a/apps/ai-studio/src/utils/export-visualization.ts b/apps/ai-studio/src/utils/export-visualization.ts
new file mode 100644
index 000000000..d32be3ba2
--- /dev/null
+++ b/apps/ai-studio/src/utils/export-visualization.ts
@@ -0,0 +1,62 @@
+import { toBlob, toPng, toSvg } from 'html-to-image';
+
+const PNG_OPTIONS = { pixelRatio: 2, backgroundColor: '#ffffff' };
+
+function triggerDownload(href: string, filename: string): void {
+ const link = document.createElement('a');
+ link.href = href;
+ link.download = filename;
+ link.click();
+}
+
+/** Render a DOM subtree to a PNG and download it. */
+export async function downloadPng(element: HTMLElement, filename = 'visualization.png'): Promise {
+ const dataUrl = await toPng(element, PNG_OPTIONS);
+ triggerDownload(dataUrl, filename);
+}
+
+/**
+ * Copy a DOM subtree to the clipboard as a PNG. Returns true if it reached the
+ * clipboard, false if it fell back to a download (e.g. Firefox, which cannot
+ * write image blobs to the clipboard).
+ */
+export async function copyImage(element: HTMLElement): Promise {
+ const blob = await toBlob(element, PNG_OPTIONS);
+ if (!blob) {
+ return false;
+ }
+ const canCopyImage = typeof ClipboardItem !== 'undefined' && Boolean(navigator.clipboard?.write);
+ if (canCopyImage) {
+ try {
+ await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
+ return true;
+ } catch {
+ // fall through to download
+ }
+ }
+ triggerDownload(URL.createObjectURL(blob), 'visualization.png');
+ return false;
+}
+
+/** Download a DOM subtree as SVG (serializes an inner when present). */
+export async function downloadSvg(element: HTMLElement, filename = 'visualization.svg'): Promise {
+ const svg = element.querySelector('svg');
+ if (svg) {
+ const serialized = new XMLSerializer().serializeToString(svg);
+ const blob = new Blob([serialized], { type: 'image/svg+xml' });
+ triggerDownload(URL.createObjectURL(blob), filename);
+ return;
+ }
+ const dataUrl = await toSvg(element);
+ triggerDownload(dataUrl, filename);
+}
+
+/** Copy the raw source text to the clipboard. */
+export async function copySource(text: string): Promise {
+ try {
+ await navigator.clipboard.writeText(text);
+ return true;
+ } catch {
+ return false;
+ }
+}
diff --git a/apps/backend/.env.example b/apps/backend/.env.example
index 5e0ded9e8..7878d3013 100644
--- a/apps/backend/.env.example
+++ b/apps/backend/.env.example
@@ -11,3 +11,16 @@ HOST=127.0.0.1
# exposing the API. Remove this line when wiring a real AuthPort. See:
# apps/backend/auth-port.decision-log.md
WB_AUTH_PORT=allow-all
+# Cloudflare Turnstile secret key (server-side). Leave empty to disable bot
+# verification (local dev). When set, POST /api/workflows/:id/execute requires a
+# valid Turnstile token sent by the frontend as the cf-turnstile-token header.
+TURNSTILE_SECRET_KEY=
+# Per-IP execution rate limit: at most EXECUTE_RATE_LIMIT runs per
+# EXECUTE_RATE_WINDOW_MS. Applies even without Turnstile.
+EXECUTE_RATE_LIMIT=10
+EXECUTE_RATE_WINDOW_MS=60000
+# OpenRouter key for the Visualize "AI adapt" endpoint (POST /api/visualize/adapt).
+# Optional: leave empty to disable AI adapt (the endpoint returns 501). The
+# execution worker keeps its own key for running workflows.
+OPENROUTER_API_KEY=
+AI_MODEL=google/gemini-2.5-flash-lite
diff --git a/apps/backend/package.json b/apps/backend/package.json
index fec96ac60..ea7844088 100644
--- a/apps/backend/package.json
+++ b/apps/backend/package.json
@@ -17,9 +17,11 @@
},
"dependencies": {
"@hono/node-server": "^1.14.0",
+ "@openrouter/ai-sdk-provider": "^2.8.0",
"@temporalio/client": "^1.11.0",
"@workflow-builder/execution-core": "workspace:*",
"@workflow-builder/types": "workspace:*",
+ "ai": "^6.0.168",
"dotenv": "^17.4.2",
"drizzle-orm": "^0.44.0",
"hono": "^4.7.0",
diff --git a/apps/backend/src/env.ts b/apps/backend/src/env.ts
index 12645d6d6..c79513b75 100644
--- a/apps/backend/src/env.ts
+++ b/apps/backend/src/env.ts
@@ -12,4 +12,15 @@ export const env = {
HOST: envOr('HOST', '127.0.0.1'),
DATABASE_URL: envOr('DATABASE_URL', 'postgresql://wb:wb@127.0.0.1:5432/workflow_builder'),
TEMPORAL_ADDRESS: envOr('TEMPORAL_ADDRESS', '127.0.0.1:7233'),
+ // Cloudflare Turnstile secret. Null = verification disabled (local dev runs
+ // unprotected). When set, workflow execution requires a valid Turnstile token.
+ TURNSTILE_SECRET_KEY: process.env['TURNSTILE_SECRET_KEY'] ?? null,
+ // Per-IP execution rate limit: at most LIMIT runs per WINDOW_MS. Applies even
+ // without Turnstile, so the public demo budget has a backstop out of the box.
+ EXECUTE_RATE_LIMIT: Number(envOr('EXECUTE_RATE_LIMIT', '10')),
+ EXECUTE_RATE_WINDOW_MS: Number(envOr('EXECUTE_RATE_WINDOW_MS', '60000')),
+ // OpenRouter for the Visualize "AI adapt" endpoint. Null = adapt disabled
+ // (the endpoint returns 501). The worker keeps its own key for execution.
+ OPENROUTER_API_KEY: process.env['OPENROUTER_API_KEY'] ?? null,
+ AI_MODEL: envOr('AI_MODEL', 'google/gemini-2.5-flash-lite'),
};
diff --git a/apps/backend/src/routes/visualize.ts b/apps/backend/src/routes/visualize.ts
new file mode 100644
index 000000000..d5f68428d
--- /dev/null
+++ b/apps/backend/src/routes/visualize.ts
@@ -0,0 +1,85 @@
+import { createOpenRouter } from '@openrouter/ai-sdk-provider';
+import { generateText } from 'ai';
+import { Hono } from 'hono';
+import { z } from 'zod';
+
+import type { AssertAuthorized, AuthVariables } from '../auth';
+import { env } from '../env';
+import { logger as backendLogger } from '../logger';
+import { guardExecution } from '../security/execution-guard';
+import type { TenantVariables } from '../tenant';
+
+const logger = backendLogger.child({ component: 'visualize-route' });
+
+const adaptSchema = z.object({
+ content: z.string().min(1).max(20_000),
+ format: z.enum(['diagram', 'chart', 'table', 'json', 'stat-cards', 'markdown', 'text']),
+});
+
+// Reshape an automation step's output into a specific visualization format. Each
+// prompt asks for ONLY the payload (no fences, no prose) and reshapes the data to
+// fit the format using only facts present in the content.
+const FORMAT_PROMPTS: Record['format'], string> = {
+ diagram: `You reshape content into a Mermaid diagram so it can be rendered as one. Choose the diagram type that best represents the content: a flowchart (\`flowchart TD\`) for processes/steps/dependencies, a \`sequenceDiagram\` for interactions over time.
+Rules:
+- Output ONLY the raw Mermaid source. No code fences, no commentary.
+- Begin with a valid declaration and direction, e.g. \`flowchart TD\`.
+- Keep node labels short. Wrap every label that contains a space or punctuation in double quotes, e.g. \`A["Fix export bug"]\`. Never put parentheses, semicolons, colons, or unescaped quotes inside a label.
+- Aim for 4-12 nodes; connect them to show the real relationships.
+- Use only facts from the content. Do not invent steps.`,
+ chart: `You reshape content into chart data so it can be rendered as a chart. Find a categorical dimension and a numeric measure in the content.
+Rules:
+- Output ONLY JSON. No code fences, no commentary.
+- Shape: a JSON array like [{"label":"Q1","value":42}], OR {"type":"bar"|"line"|"pie"|"area","data":[{"label":...,"value":...}]}.
+- "value" must be a number. Aggregate or count where the content implies it (e.g. number of items per category).
+- Produce 2-12 data points. Use real numbers from the content; if there are none, count occurrences. If the content has nothing quantifiable, output [].`,
+ table: `You reshape content into a table. Output ONLY a JSON array of flat row objects that ALL share the same keys (the columns). Use concise column names, flatten nested values to short strings, and include one object per row. No code fences, no commentary. Use only facts from the content.`,
+ json: `Output ONLY a single JSON value (object or array) that faithfully captures the structure of the content. No code fences, no commentary.`,
+ 'stat-cards': `Extract the key metrics / KPIs from the content as a flat JSON object mapping a short human label to a scalar value (string, number, or boolean), e.g. {"Open tickets":14,"Owner":"Sam"}. Use 2-8 entries, the most important first. Output ONLY JSON, no code fences, no commentary.`,
+ markdown: `Reformat the content as clean, well-structured Markdown (headings, lists, bold where it helps). Keep all the information. Output ONLY the Markdown.`,
+ text: `Return the content as clean, readable plain text. Output ONLY the text.`,
+};
+
+export function createVisualizeRoutes(
+ assertAuthorized: AssertAuthorized,
+): Hono<{ Variables: AuthVariables & TenantVariables }> {
+ const routes = new Hono<{ Variables: AuthVariables & TenantVariables }>();
+
+ // Convert an arbitrary upstream output into a specific render format via an LLM.
+ // Same abuse gate as workflow execution (per-IP rate limit + optional Turnstile).
+ routes.post('/adapt', async (c) => {
+ await assertAuthorized(c, 'workflows:execute', { kind: 'workflows' });
+
+ const blocked = await guardExecution(c);
+ if (blocked) {
+ return blocked;
+ }
+
+ if (!env.OPENROUTER_API_KEY) {
+ return c.json({ code: 'adapt_disabled', message: 'AI adapt is not configured on this server.' }, 501);
+ }
+
+ const parsed = z.safeParse(adaptSchema, await c.req.json());
+ if (!parsed.success) {
+ return c.json({ code: 'validation_error', message: 'Request body failed validation' }, 400);
+ }
+ const { content, format } = parsed.data;
+
+ try {
+ const openrouter = createOpenRouter({ apiKey: env.OPENROUTER_API_KEY });
+ const result = await generateText({
+ model: openrouter.chat(env.AI_MODEL),
+ system: FORMAT_PROMPTS[format],
+ // Low temperature for stable, well-formed structured output.
+ temperature: 0.2,
+ prompt: `Content to convert:\n\n${content}`,
+ });
+ return c.json({ output: result.text.trim() });
+ } catch (error) {
+ logger.error('adapt failed', { error: error instanceof Error ? error.message : String(error) });
+ return c.json({ code: 'adapt_failed', message: 'Could not adapt the content.' }, 502);
+ }
+ });
+
+ return routes;
+}
diff --git a/apps/backend/src/routes/workflows.ts b/apps/backend/src/routes/workflows.ts
index 20d632f81..37d3bd21c 100644
--- a/apps/backend/src/routes/workflows.ts
+++ b/apps/backend/src/routes/workflows.ts
@@ -9,6 +9,7 @@ import { mapToExecutionModel } from '../domain/mapper/from-integration-data';
import { workflowSnapshotSchema } from '../domain/mapper/snapshot-schema';
import { getWorkflowEngine } from '../engine';
import { logger as backendLogger } from '../logger';
+import { guardExecution } from '../security/execution-guard';
import type { TenantVariables } from '../tenant';
const logger = backendLogger.child({ component: 'workflows-route' });
@@ -170,6 +171,13 @@ export function createWorkflowsRoutes(
await assertAuthorized(c, 'workflows:execute', { kind: 'workflow', workflowId });
+ // Public-demo abuse control (per-IP rate limit + optional Turnstile). This
+ // is the only endpoint that spends real LLM budget, so it is the gate.
+ const blocked = await guardExecution(c);
+ if (blocked) {
+ return blocked;
+ }
+
const parsed = z.safeParse(executeSchema, await c.req.json());
if (!parsed.success) {
return c.json(
diff --git a/apps/backend/src/security/execution-guard.ts b/apps/backend/src/security/execution-guard.ts
new file mode 100644
index 000000000..fdd1b9b05
--- /dev/null
+++ b/apps/backend/src/security/execution-guard.ts
@@ -0,0 +1,78 @@
+import type { Context } from 'hono';
+
+import { env } from '../env';
+import { isTurnstileEnabled, verifyTurnstileToken } from './turnstile';
+
+type Bucket = { count: number; resetAt: number };
+
+// In-memory per-IP fixed-window counter. Fine for a single-instance demo; a
+// multi-instance deployment would back this with a shared store (e.g. Redis).
+const buckets = new Map();
+const MAX_TRACKED_IPS = 10_000;
+
+function clientIp(c: Context): string {
+ const forwarded = c.req.header('x-forwarded-for');
+ return (
+ c.req.header('cf-connecting-ip') ??
+ (forwarded ? forwarded.split(',')[0].trim() : undefined) ??
+ c.req.header('x-real-ip') ??
+ 'unknown'
+ );
+}
+
+function checkRateLimit(ip: string): { allowed: boolean; retryAfterSec: number } {
+ const now = Date.now();
+
+ // Cheap memory bound: a single window never holds enough distinct demo IPs
+ // for clearing to lose useful state.
+ if (buckets.size > MAX_TRACKED_IPS) {
+ buckets.clear();
+ }
+
+ const bucket = buckets.get(ip);
+ if (!bucket || bucket.resetAt <= now) {
+ buckets.set(ip, { count: 1, resetAt: now + env.EXECUTE_RATE_WINDOW_MS });
+ return { allowed: true, retryAfterSec: 0 };
+ }
+
+ bucket.count += 1;
+ if (bucket.count > env.EXECUTE_RATE_LIMIT) {
+ return { allowed: false, retryAfterSec: Math.ceil((bucket.resetAt - now) / 1000) };
+ }
+ return { allowed: true, retryAfterSec: 0 };
+}
+
+/**
+ * Abuse control for the single workflow-execution choke point: a per-IP rate
+ * limit plus an optional Cloudflare Turnstile check. Returns a Response to
+ * short-circuit the request, or null when the run may proceed. Both controls
+ * are no-ops when unconfigured, so local dev runs without any keys.
+ */
+export async function guardExecution(c: Context): Promise {
+ const ip = clientIp(c);
+
+ const rate = checkRateLimit(ip);
+ if (!rate.allowed) {
+ c.header('Retry-After', String(rate.retryAfterSec));
+ return c.json(
+ { code: 'rate_limited', message: 'Too many runs from this session. Please wait a moment and try again.' },
+ 429,
+ );
+ }
+
+ if (isTurnstileEnabled()) {
+ const token = c.req.header('cf-turnstile-token');
+ if (!token) {
+ return c.json({ code: 'verification_required', message: 'Bot verification is required to run a workflow.' }, 403);
+ }
+ const ok = await verifyTurnstileToken(token, ip === 'unknown' ? undefined : ip);
+ if (!ok) {
+ return c.json(
+ { code: 'verification_failed', message: 'Bot verification failed. Please reload the page and try again.' },
+ 403,
+ );
+ }
+ }
+
+ return null;
+}
diff --git a/apps/backend/src/security/turnstile.ts b/apps/backend/src/security/turnstile.ts
new file mode 100644
index 000000000..a544c0b30
--- /dev/null
+++ b/apps/backend/src/security/turnstile.ts
@@ -0,0 +1,48 @@
+import { env } from '../env';
+import { logger as backendLogger } from '../logger';
+
+const logger = backendLogger.child({ component: 'turnstile' });
+
+const SITEVERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
+
+/** True when a Turnstile secret is configured; otherwise verification is skipped. */
+export function isTurnstileEnabled(): boolean {
+ return Boolean(env.TURNSTILE_SECRET_KEY);
+}
+
+/**
+ * Verify a Cloudflare Turnstile token server-side. Returns true when disabled
+ * (no secret configured). Fails closed on a verifier/network error, since this
+ * gates a public, paid LLM run.
+ */
+export async function verifyTurnstileToken(token: string, remoteIp?: string): Promise {
+ const secret = env.TURNSTILE_SECRET_KEY;
+ if (!secret) {
+ return true;
+ }
+
+ const form = new URLSearchParams();
+ form.set('secret', secret);
+ form.set('response', token);
+ if (remoteIp) {
+ form.set('remoteip', remoteIp);
+ }
+
+ try {
+ const response = await fetch(SITEVERIFY_URL, {
+ method: 'POST',
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
+ body: form,
+ });
+ const result = (await response.json()) as { success?: boolean; 'error-codes'?: string[] };
+ if (!result.success) {
+ logger.warn('turnstile verification rejected', { errorCodes: result['error-codes'] ?? [] });
+ }
+ return result.success === true;
+ } catch (error) {
+ logger.error('turnstile verification error', {
+ error: error instanceof Error ? error.message : String(error),
+ });
+ return false;
+ }
+}
diff --git a/apps/backend/src/server.ts b/apps/backend/src/server.ts
index 351dc9a81..848e30b3b 100644
--- a/apps/backend/src/server.ts
+++ b/apps/backend/src/server.ts
@@ -15,6 +15,7 @@ import {
import { env } from './env';
import { logger } from './logger';
import { createExecutionsRoutes } from './routes/executions';
+import { createVisualizeRoutes } from './routes/visualize';
import { createWorkflowsRoutes } from './routes/workflows';
import { NoopTenantContextPort, type TenantContextPort, type TenantVariables, createTenantMiddleware } from './tenant';
@@ -62,6 +63,7 @@ app.use('/api/*', createTenantMiddleware(tenantPort));
app.route('/api/workflows', createWorkflowsRoutes(assertAuthorized));
app.route('/api/executions', createExecutionsRoutes(assertAuthorized));
+app.route('/api/visualize', createVisualizeRoutes(assertAuthorized));
serve({ fetch: app.fetch, port: env.PORT, hostname: env.HOST }, () => {
logger.info('backend listening', { url: `http://${env.HOST}:${env.PORT}` });
diff --git a/apps/execution-worker/.env.example b/apps/execution-worker/.env.example
index 9703d99c5..4a2aaf531 100644
--- a/apps/execution-worker/.env.example
+++ b/apps/execution-worker/.env.example
@@ -3,4 +3,9 @@ TEMPORAL_ADDRESS=127.0.0.1:7233
# OpenRouter — any model
OPENROUTER_API_KEY=sk-or-...
-AI_MODEL=anthropic/claude-3.5-haiku
+AI_MODEL=google/gemini-2.5-flash-lite
+
+# Tavily web search (optional). Enables the AI Agent's "Web search" tool. Get a
+# free key at https://tavily.com (free tier ~1000 searches/month). Leave empty
+# to disable: agents with web search toggled on still run, just without the tool.
+TAVILY_API_KEY=
diff --git a/apps/execution-worker/src/activities/ai-agent.ts b/apps/execution-worker/src/activities/ai-agent.ts
index 582be8056..6b70a1992 100644
--- a/apps/execution-worker/src/activities/ai-agent.ts
+++ b/apps/execution-worker/src/activities/ai-agent.ts
@@ -1,12 +1,19 @@
-import { generateText } from 'ai';
+import { generateText, stepCountIs } from 'ai';
import { type ExecutionContext, type LoggerPort, resolveTemplate } from '@workflow-builder/execution-core';
import type { AiAgentNode } from '../domain/ai-studio-nodes';
+import { createWebSearchTool } from '../tools/web-search';
+
+// Cap on the agentic tool loop: enough for a search → synthesize round-trip
+// (and a retry), bounded so a misbehaving model can't run up cost.
+const MAX_TOOL_STEPS = 4;
type AiAgentDeps = {
model: Parameters[0]['model'];
logger?: LoggerPort;
+ // Optional. When present and the node opts in, the agent gets a web-search tool.
+ tavilyApiKey?: string;
};
export async function executeAiAgent(node: AiAgentNode, context: ExecutionContext, deps: AiAgentDeps) {
@@ -36,11 +43,19 @@ export async function executeAiAgent(node: AiAgentNode, context: ExecutionContex
userPrompt = `Here is the context from previous steps:\n\n${parts.join('\n\n')}`;
}
+ // Expose the web-search tool only when the node opted in AND a key is set.
+ // Without it the agent still runs — it just answers without searching.
+ const webSearchEnabled = node.config.webSearch === true && Boolean(deps.tavilyApiKey);
+ const tools = webSearchEnabled ? { webSearch: createWebSearchTool(deps.tavilyApiKey!) } : undefined;
+
try {
const result = await generateText({
model: deps.model,
system: resolvedPrompt,
prompt: userPrompt,
+ // The AI SDK runs the tool call/execute/continue loop internally up to
+ // this many steps; no effect when `tools` is undefined.
+ ...(tools ? { tools, stopWhen: stepCountIs(MAX_TOOL_STEPS) } : {}),
});
return { output: { response: result.text } };
diff --git a/apps/execution-worker/src/domain/ai-studio-nodes.ts b/apps/execution-worker/src/domain/ai-studio-nodes.ts
index 131fb0a73..c21e67809 100644
--- a/apps/execution-worker/src/domain/ai-studio-nodes.ts
+++ b/apps/execution-worker/src/domain/ai-studio-nodes.ts
@@ -7,6 +7,7 @@ type TriggerNodeConfig = Record;
type AiAgentNodeConfig = {
systemPrompt: string; // supports {{namespace.path}} template references
+ webSearch?: boolean; // when true (and TAVILY_API_KEY is set), expose the web-search tool
};
export type DecisionBranchCondition = {
@@ -27,6 +28,10 @@ type DecisionNodeConfig = {
decisionBranches: DecisionBranch[];
};
+// Display-only node: it renders an upstream output on the canvas. Has no
+// runtime config - the UI reads the upstream node's output directly.
+type VisualizeNodeConfig = Record;
+
export type TriggerNode = {
id: string;
type: 'ai-studio/trigger';
@@ -45,4 +50,10 @@ export type DecisionNode = {
config: DecisionNodeConfig;
};
-export type AiStudioNode = TriggerNode | AiAgentNode | DecisionNode;
+type VisualizeNode = {
+ id: string;
+ type: 'ai-studio/visualize';
+ config: VisualizeNodeConfig;
+};
+
+export type AiStudioNode = TriggerNode | AiAgentNode | DecisionNode | VisualizeNode;
diff --git a/apps/execution-worker/src/engines/temporal/worker.ts b/apps/execution-worker/src/engines/temporal/worker.ts
index 326ed7c5a..ee443ff3b 100644
--- a/apps/execution-worker/src/engines/temporal/worker.ts
+++ b/apps/execution-worker/src/engines/temporal/worker.ts
@@ -10,6 +10,7 @@ import type { AiStudioNode } from '../../domain/ai-studio-nodes';
import { env } from '../../env';
import { executeDecision } from '../../executors/decision';
import { executeTrigger } from '../../executors/trigger';
+import { executeVisualize } from '../../executors/visualize';
import { logger } from '../../logger';
const { createOpenRouter } = await import('@openrouter/ai-sdk-provider');
@@ -24,7 +25,9 @@ const aiAgentLogger = logger.child({ component: 'ai-agent' });
const nodeExecutors: NodeExecutorRegistry = {
'ai-studio/trigger': executeTrigger,
'ai-studio/decision': executeDecision,
- 'ai-studio/ai-agent': (node, context) => executeAiAgent(node, context, { model, logger: aiAgentLogger }),
+ 'ai-studio/ai-agent': (node, context) =>
+ executeAiAgent(node, context, { model, logger: aiAgentLogger, tavilyApiKey: env.TAVILY_API_KEY }),
+ 'ai-studio/visualize': executeVisualize,
};
const activities = {
diff --git a/apps/execution-worker/src/env.ts b/apps/execution-worker/src/env.ts
index 019e35447..f3cbfb074 100644
--- a/apps/execution-worker/src/env.ts
+++ b/apps/execution-worker/src/env.ts
@@ -17,5 +17,10 @@ export const env = {
DATABASE_URL: envOr('DATABASE_URL', 'postgresql://wb:wb@127.0.0.1:5432/workflow_builder'),
TEMPORAL_ADDRESS: envOr('TEMPORAL_ADDRESS', '127.0.0.1:7233'),
OPENROUTER_API_KEY: requireEnv('OPENROUTER_API_KEY'),
- AI_MODEL: envOr('AI_MODEL', 'anthropic/claude-3.5-haiku'),
+ // Cheap, fast default for the public demo. Quality-per-cost is what matters
+ // here, not frontier capability — the canvas is the product, not the model.
+ AI_MODEL: envOr('AI_MODEL', 'google/gemini-2.5-flash-lite'),
+ // Optional. Enables the AI Agent's web-search tool. Without it, agents with
+ // "Web search" toggled on still run — they just answer without the tool.
+ TAVILY_API_KEY: process.env['TAVILY_API_KEY'],
};
diff --git a/apps/execution-worker/src/executors/visualize.ts b/apps/execution-worker/src/executors/visualize.ts
new file mode 100644
index 000000000..bc5d5b536
--- /dev/null
+++ b/apps/execution-worker/src/executors/visualize.ts
@@ -0,0 +1,6 @@
+// Visualize executor — display-only node. The on-canvas card renders the
+// upstream node's output in the UI; the executor only has to complete so the
+// node lights up and the reveal animation fires. It needs no inputs.
+export function executeVisualize() {
+ return { output: { visualized: true } };
+}
diff --git a/apps/execution-worker/src/tools/web-search.ts b/apps/execution-worker/src/tools/web-search.ts
new file mode 100644
index 000000000..ab89ce11f
--- /dev/null
+++ b/apps/execution-worker/src/tools/web-search.ts
@@ -0,0 +1,65 @@
+import { jsonSchema, tool } from 'ai';
+
+// Tavily is a search API built for LLM agents: it returns a short synthesized
+// answer plus clean snippets, so we feed the model far fewer tokens than raw
+// SERP JSON would. `search_depth: 'basic'` is the cheapest tier (1 credit).
+const TAVILY_ENDPOINT = 'https://api.tavily.com/search';
+const MAX_RESULTS = 5;
+
+type TavilyResult = { title: string; url: string; content: string };
+type TavilyResponse = { answer?: string; results?: TavilyResult[] };
+
+/**
+ * Web-search tool for the AI Agent node, backed by Tavily. The agent decides
+ * when to call it; the AI SDK runs the call/execute/continue loop internally
+ * (no graph cycles, so it stays compatible with the DAG runner). Search errors
+ * are returned to the model as a soft error rather than thrown, so a flaky
+ * lookup degrades the answer instead of failing the whole node.
+ */
+export function createWebSearchTool(apiKey: string) {
+ return tool({
+ description:
+ 'Search the web for current or external information. Use it when the answer needs up-to-date facts, recent events, or sources that were not provided. Returns a short answer plus the top result snippets with URLs.',
+ inputSchema: jsonSchema<{ query: string }>({
+ type: 'object',
+ properties: {
+ query: { type: 'string', description: 'The search query' },
+ },
+ required: ['query'],
+ additionalProperties: false,
+ }),
+ execute: async ({ query }) => {
+ try {
+ const response = await fetch(TAVILY_ENDPOINT, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${apiKey}`,
+ },
+ body: JSON.stringify({
+ query,
+ search_depth: 'basic',
+ max_results: MAX_RESULTS,
+ include_answer: true,
+ }),
+ });
+
+ if (!response.ok) {
+ return { error: `Web search failed (HTTP ${response.status}).` };
+ }
+
+ const data = (await response.json()) as TavilyResponse;
+ return {
+ answer: data.answer ?? '',
+ results: (data.results ?? []).slice(0, MAX_RESULTS).map((result) => ({
+ title: result.title,
+ url: result.url,
+ snippet: result.content,
+ })),
+ };
+ } catch (error) {
+ return { error: error instanceof Error ? error.message : 'Web search failed.' };
+ }
+ },
+ });
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2c154bab6..4e75fee33 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -106,7 +106,7 @@ importers:
version: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@synergycodes/overflow-ui':
specifier: 1.0.0-beta.27
- version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@workflow-builder/types':
specifier: workspace:*
version: link:../../packages/types
@@ -116,15 +116,30 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ html-to-image:
+ specifier: 1.11.11
+ version: 1.11.11
immer:
specifier: ^10.1.1
version: 10.1.1
+ mermaid:
+ specifier: ^11.15.0
+ version: 11.15.0
react:
specifier: ^19.1.0
version: 19.1.0
react-dom:
specifier: 'catalog:'
version: 19.1.0(react@19.1.0)
+ react-markdown:
+ specifier: ^10.1.0
+ version: 10.1.0(@types/react@19.1.8)(react@19.1.0)
+ recharts:
+ specifier: ^3.9.0
+ version: 3.9.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-is@19.0.0)(react@19.1.0)(redux@5.0.1)
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
zustand:
specifier: ^5.0.1
version: 5.0.3(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0))
@@ -153,6 +168,9 @@ importers:
'@hono/node-server':
specifier: ^1.14.0
version: 1.19.14(hono@4.12.14)
+ '@openrouter/ai-sdk-provider':
+ specifier: ^2.8.0
+ version: 2.8.0(ai@6.0.168(zod@4.3.6))(zod@4.3.6)
'@temporalio/client':
specifier: ^1.11.0
version: 1.16.0
@@ -162,6 +180,9 @@ importers:
'@workflow-builder/types':
specifier: workspace:*
version: link:../../packages/types
+ ai:
+ specifier: ^6.0.168
+ version: 6.0.168(zod@4.3.6)
dotenv:
specifier: ^17.4.2
version: 17.4.2
@@ -204,7 +225,7 @@ importers:
version: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@synergycodes/overflow-ui':
specifier: 1.0.0-beta.27
- version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@xyflow/react':
specifier: 'catalog:'
version: 12.10.0(@types/react@19.1.8)(immer@10.1.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -434,7 +455,7 @@ importers:
version: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@synergycodes/overflow-ui':
specifier: 1.0.0-beta.27
- version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: 1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@xyflow/react':
specifier: ^12.0.0
version: 12.10.0(@types/react@19.1.8)(immer@10.1.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -717,6 +738,9 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
+ '@braintree/sanitize-url@7.1.2':
+ resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
+
'@capsizecss/unpack@4.0.0':
resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==}
engines: {node: '>=18'}
@@ -779,6 +803,9 @@ packages:
'@changesets/write@0.4.0':
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
+ '@chevrotain/types@11.1.2':
+ resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==}
+
'@commitlint/cli@21.0.1':
resolution: {integrity: sha512-8vq10krmbJwBkvzXKhbs4o4JQEVscd3pqOlWuDUaDBwbeL694/P33UC29tZQFTAgPU9fVJ2+f2m3zw16yKWxHg==}
engines: {node: '>=22.12.0'}
@@ -1568,6 +1595,9 @@ packages:
'@iconify/utils@2.3.0':
resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==}
+ '@iconify/utils@3.1.3':
+ resolution: {integrity: sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==}
+
'@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
engines: {node: '>=18'}
@@ -1904,6 +1934,9 @@ packages:
'@mdx-js/mdx@3.1.1':
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
+ '@mermaid-js/parser@1.1.1':
+ resolution: {integrity: sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==}
+
'@microsoft/api-extractor-model@7.33.8':
resolution: {integrity: sha512-aIcoQggPyer3B6Ze3usz0YWC/oBwUHfRH5ETUsr+oT2BRA6SfTJl7IKPcPZkX4UR+PohowzW4uMxsvjrn8vm+w==}
@@ -2192,6 +2225,17 @@ packages:
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
+ '@reduxjs/toolkit@2.12.0':
+ resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==}
+ peerDependencies:
+ react: ^16.9.0 || ^17.0.0 || ^18 || ^19
+ react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-redux:
+ optional: true
+
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
engines: {node: '>=14.0.0'}
@@ -2400,6 +2444,9 @@ packages:
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+ '@standard-schema/utils@0.3.0':
+ resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0':
resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==}
engines: {node: '>=14'}
@@ -2658,24 +2705,99 @@ packages:
'@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-axis@3.0.6':
+ resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
+
+ '@types/d3-brush@3.0.6':
+ resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
+
+ '@types/d3-chord@3.0.6':
+ resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
+
'@types/d3-color@3.1.3':
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+ '@types/d3-contour@3.0.6':
+ resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-dispatch@3.0.7':
+ resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
'@types/d3-drag@3.0.7':
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+ '@types/d3-dsv@3.0.7':
+ resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-fetch@3.0.7':
+ resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+ '@types/d3-force@3.0.10':
+ resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+ '@types/d3-format@3.0.4':
+ resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+ '@types/d3-geo@3.1.0':
+ resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
+
+ '@types/d3-hierarchy@3.1.7':
+ resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
'@types/d3-interpolate@3.0.4':
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-polygon@3.0.2':
+ resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
+
+ '@types/d3-quadtree@3.0.6':
+ resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+ '@types/d3-random@3.0.3':
+ resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
+
+ '@types/d3-scale-chromatic@3.1.0':
+ resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
'@types/d3-selection@3.0.11':
resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+ '@types/d3-shape@3.1.8':
+ resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
+
+ '@types/d3-time-format@4.0.3':
+ resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
'@types/d3-transition@3.0.9':
resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
'@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
+ '@types/d3@7.4.3':
+ resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -2700,6 +2822,9 @@ packages:
'@types/express@5.0.5':
resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==}
+ '@types/geojson@7946.0.16':
+ resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -2791,6 +2916,9 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
+ '@types/use-sync-external-store@0.0.6':
+ resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
+
'@types/validator@13.15.4':
resolution: {integrity: sha512-LSFfpSnJJY9wbC0LQxgvfb+ynbHftFo0tMsFOl/J4wexLnYMmDSPaj2ZyDv3TkfL1UePxPrxOWJfbiRS8mQv7A==}
@@ -2848,6 +2976,9 @@ packages:
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
deprecated: Potential CWE-502 - Update to 1.3.1 or higher
+ '@upsetjs/venn.js@2.0.0':
+ resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
+
'@vercel/oidc@3.2.0':
resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
engines: {node: '>= 20'}
@@ -3512,6 +3643,10 @@ packages:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
common-ancestor-path@1.0.1:
resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==}
@@ -3583,6 +3718,12 @@ packages:
core-js@3.46.0:
resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==}
+ cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+
+ cose-base@2.2.0:
+ resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+
cosmiconfig-typescript-loader@6.3.0:
resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==}
engines: {node: '>=v18'}
@@ -3664,10 +3805,51 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ cytoscape-cose-bilkent@4.1.0:
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape-fcose@2.2.0:
+ resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape@3.34.0:
+ resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==}
+ engines: {node: '>=0.10'}
+
+ d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+
+ d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+
+ d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
+ d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+
d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
@@ -3676,18 +3858,88 @@ packages:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
+ d3-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+
d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
+ d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+
+ d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.2:
+ resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
+ engines: {node: '>=12'}
+
+ d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+
+ d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+
d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
+ d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+
+ d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+
+ d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+
+ d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+
+ d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
+ d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
@@ -3702,6 +3954,13 @@ packages:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
+ d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+
+ dagre-d3-es@7.0.14:
+ resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==}
+
data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
@@ -3721,8 +3980,8 @@ packages:
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
- dayjs@1.11.13:
- resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+ dayjs@1.11.21:
+ resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -3745,6 +4004,9 @@ packages:
supports-color:
optional: true
+ decimal.js-light@2.5.1:
+ resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+
decimal.js@10.5.0:
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
@@ -3769,6 +4031,9 @@ packages:
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+ delaunator@5.1.0:
+ resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -3846,6 +4111,9 @@ packages:
dompurify@3.3.0:
resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
+ dompurify@3.4.11:
+ resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==}
+
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
@@ -4514,6 +4782,9 @@ packages:
h3@1.15.5:
resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==}
+ hachure-fill@0.5.2:
+ resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
+
has-bigints@1.1.0:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
engines: {node: '>= 0.4'}
@@ -4629,6 +4900,9 @@ packages:
html-to-image@1.11.11:
resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==}
+ html-url-attributes@3.0.1:
+ resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
@@ -4699,6 +4973,9 @@ packages:
immer@10.1.1:
resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==}
+ immer@11.1.8:
+ resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==}
+
import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@@ -4732,6 +5009,13 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
+ internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
inversify@6.0.1:
resolution: {integrity: sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==}
@@ -5036,9 +5320,16 @@ packages:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
+ katex@0.16.47:
+ resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==}
+ hasBin: true
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
@@ -5062,6 +5353,12 @@ packages:
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+ layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+
+ layout-base@2.0.1:
+ resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==}
+
levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
engines: {node: '>= 0.8.0'}
@@ -5193,6 +5490,11 @@ packages:
engines: {node: '>= 18'}
hasBin: true
+ marked@16.4.2:
+ resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -5290,6 +5592,9 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
+ mermaid@11.15.0:
+ resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==}
+
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
@@ -5710,6 +6015,9 @@ packages:
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+ path-data-parser@0.1.0:
+ resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -5778,6 +6086,12 @@ packages:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
+ points-on-curve@0.2.0:
+ resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
+
+ points-on-path@0.2.1:
+ resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+
possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
@@ -5928,6 +6242,12 @@ packages:
react-is@19.0.0:
resolution: {integrity: sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==}
+ react-markdown@10.1.0:
+ resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
+ peerDependencies:
+ '@types/react': '>=18'
+ react: '>=18'
+
react-mentions-ts@5.4.7:
resolution: {integrity: sha512-bTK6joPmyvLckVf1v7vE2xSSeqvL4ZwuzFvGZpt+IrtdDOdFZjiwTUBo5920kiE6WbH/v1PP81xAo6pQ0NQ0Pg==}
engines: {node: '>=20'}
@@ -5944,6 +6264,18 @@ packages:
react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-redux@9.3.0:
+ resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==}
+ peerDependencies:
+ '@types/react': ^18.2.25 || ^19
+ react: ^18.0 || ^19
+ redux: ^5.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ redux:
+ optional: true
+
react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
@@ -6006,6 +6338,14 @@ packages:
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
engines: {node: '>= 20.19.0'}
+ recharts@3.9.0:
+ resolution: {integrity: sha512-dCEcE9y20c8H2tkVeByrAXhhnBJk6/QLbxKmn+dJUptOfc5NMjwRh1jo0vZPRLD+5dMrHrP+hPEsfbGBMfnf5Q==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
recma-build-jsx@1.0.0:
resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
@@ -6020,6 +6360,14 @@ packages:
recma-stringify@1.0.0:
resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+ redux-thunk@3.1.0:
+ resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
+ peerDependencies:
+ redux: ^5.0.0
+
+ redux@5.0.1:
+ resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
+
reflect-metadata@0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
@@ -6117,6 +6465,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ reselect@5.2.0:
+ resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -6164,11 +6515,17 @@ packages:
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
engines: {node: '>= 0.8.15'}
+ robust-predicates@3.0.3:
+ resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==}
+
rollup@4.57.1:
resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ roughjs@4.6.6:
+ resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
+
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
@@ -6179,6 +6536,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
@@ -6477,6 +6837,9 @@ packages:
stylis@4.2.0:
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
+ stylis@4.4.0:
+ resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==}
+
suf-log@2.5.3:
resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==}
@@ -6573,6 +6936,9 @@ packages:
tiny-inflate@1.0.3:
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
+ tiny-invariant@1.3.3:
+ resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
@@ -6644,6 +7010,10 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
+ ts-dedent@2.3.0:
+ resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==}
+ engines: {node: '>=6.10'}
+
tsconfck@3.1.6:
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
engines: {node: ^18 || >=20}
@@ -6960,6 +7330,9 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ victory-vendor@37.3.6:
+ resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
+
vite-bundle-analyzer@0.17.1:
resolution: {integrity: sha512-ubjLhkuRgOSBNck+6xBbQmjmh8SeLTG4alEM5PX2TNzyGhKLwWlyCz1YG0an3RQnscbhVzSb6kYteoHXhP///A==}
hasBin: true
@@ -7717,6 +8090,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
+ '@braintree/sanitize-url@7.1.2': {}
+
'@capsizecss/unpack@4.0.0':
dependencies:
fontkitten: 1.0.2
@@ -7866,6 +8241,8 @@ snapshots:
human-id: 4.1.3
prettier: 2.8.8
+ '@chevrotain/types@11.1.2': {}
+
'@commitlint/cli@21.0.1(@types/node@22.12.0)(conventional-commits-parser@6.4.0)(typescript@5.6.3)':
dependencies:
'@commitlint/format': 21.0.1
@@ -8543,6 +8920,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@iconify/utils@3.1.3':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@iconify/types': 2.0.0
+ import-meta-resolve: 4.2.0
+
'@img/colour@1.0.0':
optional: true
@@ -8837,12 +9220,12 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
- '@mantine/dates@7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ '@mantine/dates@7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mantine/hooks': 7.17.8(react@19.1.0)
clsx: 2.1.1
- dayjs: 1.11.13
+ dayjs: 1.11.21
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
@@ -8896,6 +9279,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@mermaid-js/parser@1.1.1':
+ dependencies:
+ '@chevrotain/types': 11.1.2
+
'@microsoft/api-extractor-model@7.33.8(@types/node@22.12.0)':
dependencies:
'@microsoft/tsdoc': 0.16.0
@@ -9156,6 +9543,18 @@ snapshots:
'@protobufjs/utf8@1.1.0': {}
+ '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0)':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@standard-schema/utils': 0.3.0
+ immer: 11.1.8
+ redux: 5.0.1
+ redux-thunk: 3.1.0(redux@5.0.1)
+ reselect: 5.2.0
+ optionalDependencies:
+ react: 19.1.0
+ react-redux: 9.3.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1)
+
'@rollup/pluginutils@5.3.0(rollup@4.57.1)':
dependencies:
'@types/estree': 1.0.8
@@ -9337,6 +9736,8 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
+ '@standard-schema/utils@0.3.0': {}
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.26.7)':
dependencies:
'@babel/core': 7.26.7
@@ -9488,12 +9889,12 @@ snapshots:
dependencies:
'@swc/counter': 0.1.3
- '@synergycodes/overflow-ui@1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ '@synergycodes/overflow-ui@1.0.0-beta.27(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
'@floating-ui/react': 0.26.28(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@mantine/dates': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@mantine/dates': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(dayjs@1.11.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mui/base': 5.0.0-beta.62(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@phosphor-icons/react': 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -9660,18 +10061,81 @@ snapshots:
dependencies:
'@types/node': 22.12.0
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-axis@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-brush@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-chord@3.0.6': {}
+
'@types/d3-color@3.1.3': {}
+ '@types/d3-contour@3.0.6':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-dispatch@3.0.7': {}
+
'@types/d3-drag@3.0.7':
dependencies:
'@types/d3-selection': 3.0.11
+ '@types/d3-dsv@3.0.7': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-fetch@3.0.7':
+ dependencies:
+ '@types/d3-dsv': 3.0.7
+
+ '@types/d3-force@3.0.10': {}
+
+ '@types/d3-format@3.0.4': {}
+
+ '@types/d3-geo@3.1.0':
+ dependencies:
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-hierarchy@3.1.7': {}
+
'@types/d3-interpolate@3.0.4':
dependencies:
'@types/d3-color': 3.1.3
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-polygon@3.0.2': {}
+
+ '@types/d3-quadtree@3.0.6': {}
+
+ '@types/d3-random@3.0.3': {}
+
+ '@types/d3-scale-chromatic@3.1.0': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
'@types/d3-selection@3.0.11': {}
+ '@types/d3-shape@3.1.8':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time-format@4.0.3': {}
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
'@types/d3-transition@3.0.9':
dependencies:
'@types/d3-selection': 3.0.11
@@ -9681,6 +10145,39 @@ snapshots:
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.11
+ '@types/d3@7.4.3':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-axis': 3.0.6
+ '@types/d3-brush': 3.0.6
+ '@types/d3-chord': 3.0.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-contour': 3.0.6
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-dispatch': 3.0.7
+ '@types/d3-drag': 3.0.7
+ '@types/d3-dsv': 3.0.7
+ '@types/d3-ease': 3.0.2
+ '@types/d3-fetch': 3.0.7
+ '@types/d3-force': 3.0.10
+ '@types/d3-format': 3.0.4
+ '@types/d3-geo': 3.1.0
+ '@types/d3-hierarchy': 3.1.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-path': 3.1.1
+ '@types/d3-polygon': 3.0.2
+ '@types/d3-quadtree': 3.0.6
+ '@types/d3-random': 3.0.3
+ '@types/d3-scale': 4.0.9
+ '@types/d3-scale-chromatic': 3.1.0
+ '@types/d3-selection': 3.0.11
+ '@types/d3-shape': 3.1.8
+ '@types/d3-time': 3.0.4
+ '@types/d3-time-format': 4.0.3
+ '@types/d3-timer': 3.0.2
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -9716,6 +10213,8 @@ snapshots:
'@types/express-serve-static-core': 5.1.0
'@types/serve-static': 1.15.10
+ '@types/geojson@7946.0.16': {}
+
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -9801,6 +10300,8 @@ snapshots:
'@types/unist@3.0.3': {}
+ '@types/use-sync-external-store@0.0.6': {}
+
'@types/validator@13.15.4': {}
'@types/yauzl@2.10.3':
@@ -9887,6 +10388,11 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
+ '@upsetjs/venn.js@2.0.0':
+ optionalDependencies:
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
'@vercel/oidc@3.2.0': {}
'@vitejs/plugin-react@4.3.4(vite@6.4.1(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4))':
@@ -10755,6 +11261,8 @@ snapshots:
commander@7.2.0: {}
+ commander@8.3.0: {}
+
common-ancestor-path@1.0.1: {}
compare-func@2.0.0:
@@ -10818,6 +11326,14 @@ snapshots:
core-js@3.46.0:
optional: true
+ cose-base@1.0.3:
+ dependencies:
+ layout-base: 1.0.2
+
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
cosmiconfig-typescript-loader@6.3.0(@types/node@22.12.0)(cosmiconfig@9.0.1(typescript@5.6.3))(typescript@5.6.3):
dependencies:
'@types/node': 22.12.0
@@ -10917,8 +11433,50 @@ snapshots:
csstype@3.1.3: {}
+ cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0):
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.34.0
+
+ cytoscape-fcose@2.2.0(cytoscape@3.34.0):
+ dependencies:
+ cose-base: 2.2.0
+ cytoscape: 3.34.0
+
+ cytoscape@3.34.0: {}
+
+ d3-array@2.12.1:
+ dependencies:
+ internmap: 1.0.1
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-axis@3.0.0: {}
+
+ d3-brush@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3-chord@3.0.1:
+ dependencies:
+ d3-path: 3.1.0
+
d3-color@3.1.0: {}
+ d3-contour@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.1.0
+
d3-dispatch@3.0.1: {}
d3-drag@3.0.0:
@@ -10926,14 +11484,82 @@ snapshots:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
+ d3-dsv@3.0.1:
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+
d3-ease@3.0.1: {}
+ d3-fetch@3.0.1:
+ dependencies:
+ d3-dsv: 3.0.1
+
+ d3-force@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+
+ d3-format@3.1.2: {}
+
+ d3-geo@3.1.1:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-hierarchy@3.1.2: {}
+
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
+ d3-path@1.0.9: {}
+
+ d3-path@3.1.0: {}
+
+ d3-polygon@3.0.1: {}
+
+ d3-quadtree@3.0.1: {}
+
+ d3-random@3.0.1: {}
+
+ d3-sankey@0.12.3:
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+
+ d3-scale-chromatic@3.1.0:
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
d3-selection@3.0.0: {}
+ d3-shape@1.3.7:
+ dependencies:
+ d3-path: 1.0.9
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
d3-timer@3.0.1: {}
d3-transition@3.0.1(d3-selection@3.0.0):
@@ -10953,6 +11579,44 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3@7.9.0:
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.2
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+
+ dagre-d3-es@7.0.14:
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.17.21
+
data-urls@5.0.0:
dependencies:
whatwg-mimetype: 4.0.0
@@ -10978,7 +11642,7 @@ snapshots:
date-fns@4.1.0: {}
- dayjs@1.11.13: {}
+ dayjs@1.11.21: {}
de-indent@1.0.2: {}
@@ -10990,6 +11654,8 @@ snapshots:
dependencies:
ms: 2.1.3
+ decimal.js-light@2.5.1: {}
+
decimal.js@10.5.0: {}
decode-named-character-reference@1.3.0:
@@ -11014,6 +11680,10 @@ snapshots:
defu@6.1.4: {}
+ delaunator@5.1.0:
+ dependencies:
+ robust-predicates: 3.0.3
+
delayed-stream@1.0.0: {}
depd@2.0.0: {}
@@ -11079,6 +11749,10 @@ snapshots:
'@types/trusted-types': 2.0.7
optional: true
+ dompurify@3.4.11:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
@@ -11894,6 +12568,8 @@ snapshots:
ufo: 1.6.3
uncrypto: 0.1.3
+ hachure-fill@0.5.2: {}
+
has-bigints@1.1.0: {}
has-flag@4.0.0: {}
@@ -12127,6 +12803,8 @@ snapshots:
html-to-image@1.11.11: {}
+ html-url-attributes@3.0.1: {}
+
html-void-elements@3.0.0: {}
html-whitespace-sensitive-tag-names@3.0.1: {}
@@ -12206,6 +12884,8 @@ snapshots:
immer@10.1.1: {}
+ immer@11.1.8: {}
+
import-fresh@3.3.0:
dependencies:
parent-module: 1.0.1
@@ -12231,6 +12911,10 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
+ internmap@1.0.1: {}
+
+ internmap@2.0.3: {}
+
inversify@6.0.1: {}
iobuffer@5.4.0: {}
@@ -12555,10 +13239,16 @@ snapshots:
object.assign: 4.1.7
object.values: 1.2.1
+ katex@0.16.47:
+ dependencies:
+ commander: 8.3.0
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
+ khroma@2.1.0: {}
+
kleur@3.0.3: {}
kleur@4.1.5: {}
@@ -12585,6 +13275,10 @@ snapshots:
kolorist@1.8.0: {}
+ layout-base@1.0.2: {}
+
+ layout-base@2.0.1: {}
+
levn@0.3.0:
dependencies:
prelude-ls: 1.1.2
@@ -12720,6 +13414,8 @@ snapshots:
marked@15.0.12: {}
+ marked@16.4.2: {}
+
math-intrinsics@1.1.0: {}
md5@2.3.0:
@@ -12946,6 +13642,30 @@ snapshots:
merge2@1.4.1: {}
+ mermaid@11.15.0:
+ dependencies:
+ '@braintree/sanitize-url': 7.1.2
+ '@iconify/utils': 3.1.3
+ '@mermaid-js/parser': 1.1.1
+ '@types/d3': 7.4.3
+ '@upsetjs/venn.js': 2.0.0
+ cytoscape: 3.34.0
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0)
+ cytoscape-fcose: 2.2.0(cytoscape@3.34.0)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.14
+ dayjs: 1.11.21
+ dompurify: 3.4.11
+ es-toolkit: 1.46.1
+ katex: 0.16.47
+ khroma: 2.1.0
+ marked: 16.4.2
+ roughjs: 4.6.6
+ stylis: 4.4.0
+ ts-dedent: 2.3.0
+ uuid: 11.1.0
+
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.3.0
@@ -13559,6 +14279,8 @@ snapshots:
path-browserify@1.0.1: {}
+ path-data-parser@0.1.0: {}
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -13606,6 +14328,13 @@ snapshots:
pluralize@8.0.0: {}
+ points-on-curve@0.2.0: {}
+
+ points-on-path@0.2.1:
+ dependencies:
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+
possible-typed-array-names@1.0.0: {}
postcss-nested@6.2.0(postcss@8.5.6):
@@ -13764,6 +14493,24 @@ snapshots:
react-is@19.0.0: {}
+ react-markdown@10.1.0(@types/react@19.1.8)(react@19.1.0):
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.4
+ '@types/react': 19.1.8
+ devlop: 1.1.0
+ hast-util-to-jsx-runtime: 2.3.6
+ html-url-attributes: 3.0.1
+ mdast-util-to-hast: 13.2.1
+ react: 19.1.0
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ unified: 11.0.5
+ unist-util-visit: 5.1.0
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
react-mentions-ts@5.4.7(class-variance-authority@0.7.1)(clsx@2.1.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwind-merge@3.5.0):
dependencies:
class-variance-authority: 0.7.1
@@ -13777,6 +14524,15 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
+ react-redux@9.3.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1):
+ dependencies:
+ '@types/use-sync-external-store': 0.0.6
+ react: 19.1.0
+ use-sync-external-store: 1.4.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.8
+ redux: 5.0.1
+
react-refresh@0.14.2: {}
react-remove-scroll-bar@2.3.8(@types/react@19.1.8)(react@19.1.0):
@@ -13837,6 +14593,26 @@ snapshots:
readdirp@5.0.0: {}
+ recharts@3.9.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-is@19.0.0)(react@19.1.0)(redux@5.0.1):
+ dependencies:
+ '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
+ clsx: 2.1.1
+ decimal.js-light: 2.5.1
+ es-toolkit: 1.46.1
+ eventemitter3: 5.0.1
+ immer: 10.1.1
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-is: 19.0.0
+ react-redux: 9.3.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1)
+ reselect: 5.2.0
+ tiny-invariant: 1.3.3
+ use-sync-external-store: 1.4.0(react@19.1.0)
+ victory-vendor: 37.3.6
+ transitivePeerDependencies:
+ - '@types/react'
+ - redux
+
recma-build-jsx@1.0.0:
dependencies:
'@types/estree': 1.0.8
@@ -13866,6 +14642,12 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ redux-thunk@3.1.0(redux@5.0.1):
+ dependencies:
+ redux: 5.0.1
+
+ redux@5.0.1: {}
+
reflect-metadata@0.1.13: {}
reflect.getprototypeof@1.0.10:
@@ -14029,6 +14811,8 @@ snapshots:
require-from-string@2.0.2: {}
+ reselect@5.2.0: {}
+
resolve-from@4.0.0: {}
resolve-from@5.0.0: {}
@@ -14084,6 +14868,8 @@ snapshots:
rgbcolor@1.0.1:
optional: true
+ robust-predicates@3.0.3: {}
+
rollup@4.57.1:
dependencies:
'@types/estree': 1.0.8
@@ -14115,6 +14901,13 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.57.1
fsevents: 2.3.3
+ roughjs@4.6.6:
+ dependencies:
+ hachure-fill: 0.5.2
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+ points-on-path: 0.2.1
+
router@2.2.0:
dependencies:
debug: 4.4.3
@@ -14131,6 +14924,8 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
+ rw@1.3.3: {}
+
rxjs@7.8.1:
dependencies:
tslib: 2.8.1
@@ -14504,6 +15299,8 @@ snapshots:
stylis@4.2.0: {}
+ stylis@4.4.0: {}
+
suf-log@2.5.3:
dependencies:
s.color: 0.0.15
@@ -14599,6 +15396,8 @@ snapshots:
tiny-inflate@1.0.3: {}
+ tiny-invariant@1.3.3: {}
+
tinybench@2.9.0: {}
tinyexec@0.3.2: {}
@@ -14650,6 +15449,8 @@ snapshots:
dependencies:
typescript: 5.6.3
+ ts-dedent@2.3.0: {}
+
tsconfck@3.1.6(typescript@5.6.3):
optionalDependencies:
typescript: 5.6.3
@@ -14939,6 +15740,23 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ victory-vendor@37.3.6:
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-ease': 3.0.2
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-shape': 3.1.8
+ '@types/d3-time': 3.0.4
+ '@types/d3-timer': 3.0.2
+ d3-array: 3.2.4
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-timer: 3.0.1
+
vite-bundle-analyzer@0.17.1: {}
vite-node@3.0.4(@types/node@22.12.0)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.4):