App.tsx
Browse files- .dockerignore +9 -0
- .gitignore +9 -0
- Dockerfile +28 -0
- README.md +73 -6
- api/main.py +544 -0
- build.sh +30 -0
- frontend/index.html +16 -0
- frontend/package.json +46 -0
- frontend/pnpm-lock.yaml +2631 -0
- frontend/src/App.tsx +29 -0
- frontend/src/components/AstTreeView.tsx +303 -0
- frontend/src/components/FormatterPanel.tsx +98 -0
- frontend/src/components/InjectionPanel.tsx +176 -0
- frontend/src/components/LintPanel.tsx +118 -0
- frontend/src/components/SqlEditor.tsx +173 -0
- frontend/src/index.css +88 -0
- frontend/src/lib/api.ts +114 -0
- frontend/src/main.tsx +10 -0
- frontend/src/pages/Analyzer.tsx +366 -0
- frontend/src/pages/NotFound.tsx +13 -0
- frontend/src/pages/SwaggerPage.tsx +88 -0
- frontend/tsconfig.json +25 -0
- frontend/vite.config.ts +27 -0
- requirements.txt +4 -0
.dockerignore
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
frontend/node_modules
|
| 2 |
+
frontend/.pnpm-store
|
| 3 |
+
api/static/
|
| 4 |
+
**/__pycache__
|
| 5 |
+
**/*.pyc
|
| 6 |
+
**/*.pyo
|
| 7 |
+
.git
|
| 8 |
+
.gitignore
|
| 9 |
+
*.md
|
.gitignore
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
frontend/node_modules/
|
| 2 |
+
frontend/.pnpm-store/
|
| 3 |
+
api/static/
|
| 4 |
+
__pycache__/
|
| 5 |
+
*.pyc
|
| 6 |
+
*.pyo
|
| 7 |
+
.env
|
| 8 |
+
*.log
|
| 9 |
+
dist/
|
Dockerfile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
# Install Node.js 22 for building the frontend
|
| 4 |
+
RUN apt-get update && apt-get install -y curl && \
|
| 5 |
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
|
| 6 |
+
apt-get install -y nodejs && \
|
| 7 |
+
npm install -g pnpm && \
|
| 8 |
+
apt-get clean && rm -rf /var/lib/apt/lists/*
|
| 9 |
+
|
| 10 |
+
WORKDIR /app
|
| 11 |
+
|
| 12 |
+
# Copy and install Python dependencies first (layer cache)
|
| 13 |
+
COPY requirements.txt .
|
| 14 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 15 |
+
|
| 16 |
+
# Copy frontend source and build it
|
| 17 |
+
COPY frontend/ ./frontend/
|
| 18 |
+
RUN cd frontend && pnpm install --frozen-lockfile && pnpm build
|
| 19 |
+
|
| 20 |
+
# Copy the FastAPI app (static/ was built into api/static/ by vite)
|
| 21 |
+
COPY api/ ./api/
|
| 22 |
+
|
| 23 |
+
# HF Spaces runs on port 7860
|
| 24 |
+
EXPOSE 7860
|
| 25 |
+
|
| 26 |
+
# Run the FastAPI server
|
| 27 |
+
WORKDIR /app/api
|
| 28 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
|
@@ -1,11 +1,78 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
-
|
| 9 |
---
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: SQL Analyzer
|
| 3 |
+
emoji: 🔍
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
short_description: SQL linter, AST explorer, formatter & injection detector powered by SQLFluff
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# SQL Analyzer — Linter, AST Explorer & Injection Detector
|
| 12 |
+
|
| 13 |
+
An interactive SQL analysis tool powered by **SQLFluff 4.x**, built as a pure Python FastAPI backend serving a React frontend.
|
| 14 |
+
|
| 15 |
+
## Features
|
| 16 |
+
|
| 17 |
+
- **SQL Linter** — SQLFluff rule violations with severity, rule codes, line/column info, and fixable indicators
|
| 18 |
+
- **AST Tree Explorer** — Interactive collapsible/expandable parse tree with search, filter, and color-coded node types
|
| 19 |
+
- **SQL Injection Detector** — Detects tautologies, stacked queries, UNION exfiltration, comment bypasses, and more
|
| 20 |
+
- **SQL Formatter** — Auto-fix and format SQL with copy-to-clipboard and apply-to-editor
|
| 21 |
+
- **Swagger UI** — Full OpenAPI 3.1 documentation at `/swagger`
|
| 22 |
+
- **17 SQL dialects** — ANSI, PostgreSQL, MySQL, T-SQL, SQLite, BigQuery, Snowflake, Redshift, DuckDB, Hive, Spark SQL, Trino, Databricks, Oracle, Teradata, ClickHouse, Athena
|
| 23 |
+
|
| 24 |
+
## Architecture
|
| 25 |
+
|
| 26 |
+
```
|
| 27 |
+
sql-analyzer-standalone/
|
| 28 |
+
├── api/
|
| 29 |
+
│ ├── main.py ← FastAPI app (REST endpoints + static file serving)
|
| 30 |
+
│ └── static/ ← Built React bundle (generated by pnpm build)
|
| 31 |
+
├── frontend/
|
| 32 |
+
│ ├── src/ ← React 19 + TypeScript source
|
| 33 |
+
│ ├── package.json
|
| 34 |
+
│ └── vite.config.ts ← Builds into ../api/static/
|
| 35 |
+
├── requirements.txt
|
| 36 |
+
├── Dockerfile
|
| 37 |
+
└── README.md
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Local Development
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
# 1. Install Python deps
|
| 44 |
+
pip install -r requirements.txt
|
| 45 |
+
|
| 46 |
+
# 2. Build the React frontend
|
| 47 |
+
cd frontend && pnpm install && pnpm build && cd ..
|
| 48 |
+
|
| 49 |
+
# 3. Start the FastAPI server
|
| 50 |
+
cd api && uvicorn main:app --reload --port 7860
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
Open http://localhost:7860
|
| 54 |
+
|
| 55 |
+
## API Endpoints
|
| 56 |
+
|
| 57 |
+
| Method | Path | Description |
|
| 58 |
+
|--------|------|-------------|
|
| 59 |
+
| GET | `/api/health` | Health check + SQLFluff version |
|
| 60 |
+
| POST | `/api/lint` | Lint SQL with SQLFluff |
|
| 61 |
+
| POST | `/api/parse` | Parse SQL into AST |
|
| 62 |
+
| POST | `/api/format` | Format/fix SQL |
|
| 63 |
+
| POST | `/api/inject` | Detect SQL injection patterns |
|
| 64 |
+
| GET | `/openapi.json` | OpenAPI schema |
|
| 65 |
+
| GET | `/docs` | Swagger UI (FastAPI built-in) |
|
| 66 |
+
| GET | `/swagger` | Custom Swagger UI page |
|
| 67 |
+
|
| 68 |
+
## Request Format
|
| 69 |
+
|
| 70 |
+
All POST endpoints accept:
|
| 71 |
+
```json
|
| 72 |
+
{
|
| 73 |
+
"sql": "SELECT * FROM users WHERE id = 1",
|
| 74 |
+
"dialect": "ansi"
|
| 75 |
+
}
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
Supported dialects: `ansi`, `postgres`, `mysql`, `tsql`, `sqlite`, `bigquery`, `snowflake`, `redshift`, `duckdb`, `hive`, `sparksql`, `trino`, `databricks`, `oracle`, `teradata`, `clickhouse`, `athena`
|
api/main.py
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
SQL Analyzer — Pure FastAPI Backend
|
| 3 |
+
====================================
|
| 4 |
+
Serves:
|
| 5 |
+
• REST API → /api/health, /api/lint, /api/parse, /api/format, /api/inject
|
| 6 |
+
• Swagger UI → /docs (auto-generated by FastAPI)
|
| 7 |
+
• React SPA → everything else (static files from ./static/)
|
| 8 |
+
|
| 9 |
+
Single-process deployment — no Node.js required.
|
| 10 |
+
Compatible with Hugging Face Spaces (Docker SDK) and any OCI-compatible host.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import re
|
| 14 |
+
import json
|
| 15 |
+
import traceback
|
| 16 |
+
import os
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
from typing import Any, Optional
|
| 19 |
+
|
| 20 |
+
from fastapi import FastAPI, HTTPException, Request
|
| 21 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 22 |
+
from fastapi.responses import FileResponse, JSONResponse
|
| 23 |
+
from fastapi.staticfiles import StaticFiles
|
| 24 |
+
from pydantic import BaseModel
|
| 25 |
+
import sqlfluff
|
| 26 |
+
from sqlfluff.core import Linter
|
| 27 |
+
from sqlfluff.core.parser import BaseSegment
|
| 28 |
+
|
| 29 |
+
# ---------------------------------------------------------------------------
|
| 30 |
+
# App setup
|
| 31 |
+
# ---------------------------------------------------------------------------
|
| 32 |
+
|
| 33 |
+
app = FastAPI(
|
| 34 |
+
title="SQL Analyzer API",
|
| 35 |
+
description=(
|
| 36 |
+
"A powerful SQL analysis backend providing linting, AST parsing, "
|
| 37 |
+
"SQL formatting, and injection detection powered by SQLFluff."
|
| 38 |
+
),
|
| 39 |
+
version="1.0.0",
|
| 40 |
+
docs_url="/docs",
|
| 41 |
+
redoc_url="/redoc",
|
| 42 |
+
openapi_url="/openapi.json",
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
app.add_middleware(
|
| 46 |
+
CORSMiddleware,
|
| 47 |
+
allow_origins=["*"],
|
| 48 |
+
allow_credentials=True,
|
| 49 |
+
allow_methods=["*"],
|
| 50 |
+
allow_headers=["*"],
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
# ---------------------------------------------------------------------------
|
| 54 |
+
# Request / Response models
|
| 55 |
+
# ---------------------------------------------------------------------------
|
| 56 |
+
|
| 57 |
+
class SqlRequest(BaseModel):
|
| 58 |
+
sql: str
|
| 59 |
+
dialect: str = "ansi"
|
| 60 |
+
|
| 61 |
+
class LintViolation(BaseModel):
|
| 62 |
+
line_no: int
|
| 63 |
+
line_pos: int
|
| 64 |
+
code: str
|
| 65 |
+
description: str
|
| 66 |
+
name: str
|
| 67 |
+
warning: bool
|
| 68 |
+
fixable: bool
|
| 69 |
+
|
| 70 |
+
class LintResponse(BaseModel):
|
| 71 |
+
dialect: str
|
| 72 |
+
violations: list[LintViolation]
|
| 73 |
+
passed: bool
|
| 74 |
+
stats: dict[str, Any]
|
| 75 |
+
|
| 76 |
+
class AstNode(BaseModel):
|
| 77 |
+
id: str
|
| 78 |
+
type: str
|
| 79 |
+
name: str
|
| 80 |
+
raw: Optional[str]
|
| 81 |
+
start_line: Optional[int]
|
| 82 |
+
start_pos: Optional[int]
|
| 83 |
+
end_line: Optional[int]
|
| 84 |
+
end_pos: Optional[int]
|
| 85 |
+
is_leaf: bool
|
| 86 |
+
children: list["AstNode"]
|
| 87 |
+
|
| 88 |
+
AstNode.model_rebuild()
|
| 89 |
+
|
| 90 |
+
class ParseResponse(BaseModel):
|
| 91 |
+
dialect: str
|
| 92 |
+
tree: AstNode
|
| 93 |
+
token_count: int
|
| 94 |
+
depth: int
|
| 95 |
+
|
| 96 |
+
class FormatResponse(BaseModel):
|
| 97 |
+
dialect: str
|
| 98 |
+
original: str
|
| 99 |
+
formatted: str
|
| 100 |
+
changed: bool
|
| 101 |
+
fixes_applied: int
|
| 102 |
+
|
| 103 |
+
class InjectionPattern(BaseModel):
|
| 104 |
+
pattern_id: str
|
| 105 |
+
risk_level: str # critical | high | medium | low
|
| 106 |
+
category: str
|
| 107 |
+
description: str
|
| 108 |
+
detail: str
|
| 109 |
+
offending_token: Optional[str]
|
| 110 |
+
line_no: Optional[int]
|
| 111 |
+
line_pos: Optional[int]
|
| 112 |
+
recommendation: str
|
| 113 |
+
|
| 114 |
+
class InjectionResponse(BaseModel):
|
| 115 |
+
dialect: str
|
| 116 |
+
safe: bool
|
| 117 |
+
risk_score: int # 0-100
|
| 118 |
+
patterns: list[InjectionPattern]
|
| 119 |
+
summary: str
|
| 120 |
+
|
| 121 |
+
class HealthResponse(BaseModel):
|
| 122 |
+
status: str
|
| 123 |
+
version: str
|
| 124 |
+
dialects: list[str]
|
| 125 |
+
|
| 126 |
+
# ---------------------------------------------------------------------------
|
| 127 |
+
# Helpers
|
| 128 |
+
# ---------------------------------------------------------------------------
|
| 129 |
+
|
| 130 |
+
SQLFLUFF_DIALECTS = [
|
| 131 |
+
"ansi", "athena", "bigquery", "clickhouse", "databricks", "db2",
|
| 132 |
+
"duckdb", "exasol", "greenplum", "hive", "mysql", "oracle",
|
| 133 |
+
"postgres", "redshift", "snowflake", "soql", "sparksql", "sqlite",
|
| 134 |
+
"teradata", "trino", "tsql",
|
| 135 |
+
]
|
| 136 |
+
|
| 137 |
+
_node_counter = 0
|
| 138 |
+
|
| 139 |
+
def _reset_counter():
|
| 140 |
+
global _node_counter
|
| 141 |
+
_node_counter = 0
|
| 142 |
+
|
| 143 |
+
def _next_id() -> str:
|
| 144 |
+
global _node_counter
|
| 145 |
+
_node_counter += 1
|
| 146 |
+
return f"node_{_node_counter}"
|
| 147 |
+
|
| 148 |
+
def _segment_to_node(seg: BaseSegment, depth: int = 0) -> AstNode:
|
| 149 |
+
"""Recursively convert a SQLFluff segment into a serialisable AstNode."""
|
| 150 |
+
is_raw_attr = getattr(seg, "is_raw", None)
|
| 151 |
+
if callable(is_raw_attr):
|
| 152 |
+
is_leaf: bool = is_raw_attr()
|
| 153 |
+
elif is_raw_attr is not None:
|
| 154 |
+
is_leaf = bool(is_raw_attr)
|
| 155 |
+
else:
|
| 156 |
+
is_leaf = not bool(seg.segments)
|
| 157 |
+
|
| 158 |
+
raw = seg.raw if is_leaf else None
|
| 159 |
+
|
| 160 |
+
start_line = start_pos = end_line = end_pos = None
|
| 161 |
+
try:
|
| 162 |
+
if hasattr(seg, "pos_marker") and seg.pos_marker:
|
| 163 |
+
pm = seg.pos_marker
|
| 164 |
+
start_line = pm.line_no
|
| 165 |
+
start_pos = pm.line_pos
|
| 166 |
+
except Exception:
|
| 167 |
+
pass
|
| 168 |
+
|
| 169 |
+
children = []
|
| 170 |
+
if not is_leaf and seg.segments:
|
| 171 |
+
for child in seg.segments:
|
| 172 |
+
children.append(_segment_to_node(child, depth + 1))
|
| 173 |
+
|
| 174 |
+
return AstNode(
|
| 175 |
+
id=_next_id(),
|
| 176 |
+
type=seg.get_type(),
|
| 177 |
+
name=type(seg).__name__,
|
| 178 |
+
raw=raw,
|
| 179 |
+
start_line=start_line,
|
| 180 |
+
start_pos=start_pos,
|
| 181 |
+
end_line=end_line,
|
| 182 |
+
end_pos=end_pos,
|
| 183 |
+
is_leaf=bool(is_leaf),
|
| 184 |
+
children=children,
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
def _tree_depth(node: AstNode) -> int:
|
| 188 |
+
if not node.children:
|
| 189 |
+
return 0
|
| 190 |
+
return 1 + max(_tree_depth(c) for c in node.children)
|
| 191 |
+
|
| 192 |
+
def _count_tokens(node: AstNode) -> int:
|
| 193 |
+
if node.is_leaf:
|
| 194 |
+
return 1
|
| 195 |
+
return sum(_count_tokens(c) for c in node.children)
|
| 196 |
+
|
| 197 |
+
# ---------------------------------------------------------------------------
|
| 198 |
+
# Injection detection patterns
|
| 199 |
+
# ---------------------------------------------------------------------------
|
| 200 |
+
|
| 201 |
+
INJECTION_PATTERNS = [
|
| 202 |
+
{
|
| 203 |
+
"id": "tautology_or_1_1",
|
| 204 |
+
"risk": "critical",
|
| 205 |
+
"category": "Tautology",
|
| 206 |
+
"description": "Always-true tautology detected (e.g. OR 1=1)",
|
| 207 |
+
"detail": "Tautologies force WHERE clauses to always evaluate to TRUE, bypassing all filters.",
|
| 208 |
+
"recommendation": "Use parameterised queries. Never interpolate user input into SQL strings.",
|
| 209 |
+
"regex": r"\bOR\s+['\"]?\d+['\"]?\s*=\s*['\"]?\d+['\"]?",
|
| 210 |
+
"flags": re.IGNORECASE,
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"id": "tautology_or_true",
|
| 214 |
+
"risk": "critical",
|
| 215 |
+
"category": "Tautology",
|
| 216 |
+
"description": "Always-true boolean tautology (e.g. OR TRUE / OR 'a'='a')",
|
| 217 |
+
"detail": "Boolean tautologies bypass WHERE conditions entirely.",
|
| 218 |
+
"recommendation": "Use parameterised queries and validate all user-supplied data.",
|
| 219 |
+
"regex": r"\bOR\s+(?:TRUE|'[^']*'\s*=\s*'[^']*')",
|
| 220 |
+
"flags": re.IGNORECASE,
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"id": "stacked_queries",
|
| 224 |
+
"risk": "critical",
|
| 225 |
+
"category": "Stacked Queries",
|
| 226 |
+
"description": "Stacked / batched query detected (semicolon followed by another statement)",
|
| 227 |
+
"detail": "Stacked queries allow attackers to append arbitrary SQL statements.",
|
| 228 |
+
"recommendation": "Disallow multiple statements in a single query. Use stored procedures or ORMs.",
|
| 229 |
+
"regex": r";\s*(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE|UNION)\b",
|
| 230 |
+
"flags": re.IGNORECASE,
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"id": "comment_bypass_inline",
|
| 234 |
+
"risk": "high",
|
| 235 |
+
"category": "Comment Bypass",
|
| 236 |
+
"description": "Inline comment used to truncate or bypass query logic (--)",
|
| 237 |
+
"detail": "Inline comments (--) can strip the remainder of a query, bypassing authentication checks.",
|
| 238 |
+
"recommendation": "Strip or reject SQL comment sequences from user input.",
|
| 239 |
+
"regex": r"--[^\n]*",
|
| 240 |
+
"flags": 0,
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"id": "comment_bypass_block",
|
| 244 |
+
"risk": "high",
|
| 245 |
+
"category": "Comment Bypass",
|
| 246 |
+
"description": "Block comment used to obfuscate or bypass query logic (/* ... */)",
|
| 247 |
+
"detail": "Block comments can hide injected code and bypass naive input filters.",
|
| 248 |
+
"recommendation": "Strip or reject SQL block comment sequences from user input.",
|
| 249 |
+
"regex": r"/\*.*?\*/",
|
| 250 |
+
"flags": re.DOTALL,
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"id": "union_select",
|
| 254 |
+
"risk": "high",
|
| 255 |
+
"category": "UNION-based Injection",
|
| 256 |
+
"description": "UNION SELECT detected — potential data exfiltration vector",
|
| 257 |
+
"detail": "UNION SELECT allows attackers to append result sets from other tables, leaking sensitive data.",
|
| 258 |
+
"recommendation": "Use parameterised queries. Validate column counts and types.",
|
| 259 |
+
"regex": r"\bUNION\s+(?:ALL\s+)?SELECT\b",
|
| 260 |
+
"flags": re.IGNORECASE,
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"id": "sleep_benchmark",
|
| 264 |
+
"risk": "high",
|
| 265 |
+
"category": "Time-based Blind Injection",
|
| 266 |
+
"description": "Time-delay function detected (SLEEP / BENCHMARK / WAITFOR)",
|
| 267 |
+
"detail": "Time-based blind injection uses delays to infer data without visible output.",
|
| 268 |
+
"recommendation": "Parameterise all queries and restrict execution of time-delay functions.",
|
| 269 |
+
"regex": r"\b(?:SLEEP|BENCHMARK|WAITFOR\s+DELAY|PG_SLEEP)\s*\(",
|
| 270 |
+
"flags": re.IGNORECASE,
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"id": "exec_xp_cmdshell",
|
| 274 |
+
"risk": "critical",
|
| 275 |
+
"category": "Command Execution",
|
| 276 |
+
"description": "xp_cmdshell or EXEC detected — OS command execution risk",
|
| 277 |
+
"detail": "xp_cmdshell allows execution of arbitrary OS commands from SQL Server.",
|
| 278 |
+
"recommendation": "Disable xp_cmdshell. Never allow user input to reach EXEC statements.",
|
| 279 |
+
"regex": r"\b(?:xp_cmdshell|EXEC(?:UTE)?)\s*\(",
|
| 280 |
+
"flags": re.IGNORECASE,
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"id": "drop_table",
|
| 284 |
+
"risk": "critical",
|
| 285 |
+
"category": "Destructive Statement",
|
| 286 |
+
"description": "DROP TABLE / DROP DATABASE detected",
|
| 287 |
+
"detail": "Injected DROP statements can destroy entire tables or databases.",
|
| 288 |
+
"recommendation": "Restrict DDL permissions. Use parameterised queries and least-privilege accounts.",
|
| 289 |
+
"regex": r"\bDROP\s+(?:TABLE|DATABASE|SCHEMA|INDEX)\b",
|
| 290 |
+
"flags": re.IGNORECASE,
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
"id": "hex_encoding",
|
| 294 |
+
"risk": "medium",
|
| 295 |
+
"category": "Obfuscation",
|
| 296 |
+
"description": "Hex-encoded string literal detected (0x...)",
|
| 297 |
+
"detail": "Hex encoding is commonly used to bypass string-based input filters.",
|
| 298 |
+
"recommendation": "Validate and sanitise all input. Use parameterised queries.",
|
| 299 |
+
"regex": r"\b0x[0-9a-fA-F]{4,}\b",
|
| 300 |
+
"flags": 0,
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"id": "char_concat",
|
| 304 |
+
"risk": "medium",
|
| 305 |
+
"category": "Obfuscation",
|
| 306 |
+
"description": "CHAR() concatenation detected — common obfuscation technique",
|
| 307 |
+
"detail": "Attackers use CHAR() to build strings character-by-character to evade filters.",
|
| 308 |
+
"recommendation": "Use parameterised queries. Restrict use of CHAR() in user-facing contexts.",
|
| 309 |
+
"regex": r"\bCHAR\s*\(\s*\d+",
|
| 310 |
+
"flags": re.IGNORECASE,
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"id": "information_schema",
|
| 314 |
+
"risk": "medium",
|
| 315 |
+
"category": "Schema Reconnaissance",
|
| 316 |
+
"description": "INFORMATION_SCHEMA query detected — schema enumeration attempt",
|
| 317 |
+
"detail": "Attackers query INFORMATION_SCHEMA to enumerate tables, columns, and credentials.",
|
| 318 |
+
"recommendation": "Restrict access to INFORMATION_SCHEMA. Use least-privilege DB accounts.",
|
| 319 |
+
"regex": r"\bINFORMATION_SCHEMA\b",
|
| 320 |
+
"flags": re.IGNORECASE,
|
| 321 |
+
},
|
| 322 |
+
{
|
| 323 |
+
"id": "load_file",
|
| 324 |
+
"risk": "critical",
|
| 325 |
+
"category": "File System Access",
|
| 326 |
+
"description": "LOAD_FILE() or INTO OUTFILE detected — file system access risk",
|
| 327 |
+
"detail": "These MySQL functions allow reading/writing arbitrary files on the server.",
|
| 328 |
+
"recommendation": "Disable FILE privilege. Never allow user input near file I/O functions.",
|
| 329 |
+
"regex": r"\b(?:LOAD_FILE|INTO\s+(?:OUT|DUMP)FILE)\b",
|
| 330 |
+
"flags": re.IGNORECASE,
|
| 331 |
+
},
|
| 332 |
+
]
|
| 333 |
+
|
| 334 |
+
RISK_SCORE = {"critical": 35, "high": 20, "medium": 10, "low": 5}
|
| 335 |
+
|
| 336 |
+
def _detect_injection(sql: str) -> list[InjectionPattern]:
|
| 337 |
+
results: list[InjectionPattern] = []
|
| 338 |
+
for pat in INJECTION_PATTERNS:
|
| 339 |
+
for m in re.finditer(pat["regex"], sql, pat["flags"]):
|
| 340 |
+
line_no = sql[: m.start()].count("\n") + 1
|
| 341 |
+
line_pos = m.start() - sql[: m.start()].rfind("\n")
|
| 342 |
+
results.append(
|
| 343 |
+
InjectionPattern(
|
| 344 |
+
pattern_id=pat["id"],
|
| 345 |
+
risk_level=pat["risk"],
|
| 346 |
+
category=pat["category"],
|
| 347 |
+
description=pat["description"],
|
| 348 |
+
detail=pat["detail"],
|
| 349 |
+
offending_token=m.group(0)[:120],
|
| 350 |
+
line_no=line_no,
|
| 351 |
+
line_pos=line_pos,
|
| 352 |
+
recommendation=pat["recommendation"],
|
| 353 |
+
)
|
| 354 |
+
)
|
| 355 |
+
return results
|
| 356 |
+
|
| 357 |
+
# ---------------------------------------------------------------------------
|
| 358 |
+
# API Routes (all under /api/ prefix)
|
| 359 |
+
# ---------------------------------------------------------------------------
|
| 360 |
+
|
| 361 |
+
@app.get("/api/health", response_model=HealthResponse, tags=["System"])
|
| 362 |
+
def health():
|
| 363 |
+
"""Return API health status and SQLFluff version."""
|
| 364 |
+
return HealthResponse(
|
| 365 |
+
status="ok",
|
| 366 |
+
version=sqlfluff.__version__,
|
| 367 |
+
dialects=SQLFLUFF_DIALECTS,
|
| 368 |
+
)
|
| 369 |
+
|
| 370 |
+
@app.post("/api/lint", response_model=LintResponse, tags=["Analysis"])
|
| 371 |
+
def lint_sql(req: SqlRequest):
|
| 372 |
+
"""
|
| 373 |
+
Lint SQL using SQLFluff and return rule violations.
|
| 374 |
+
|
| 375 |
+
Returns a list of violations with line/column info, rule codes,
|
| 376 |
+
severity, and whether each violation is auto-fixable.
|
| 377 |
+
"""
|
| 378 |
+
try:
|
| 379 |
+
dialect = req.dialect if req.dialect in SQLFLUFF_DIALECTS else "ansi"
|
| 380 |
+
linter = Linter(dialect=dialect)
|
| 381 |
+
result = linter.lint_string(req.sql)
|
| 382 |
+
violations = []
|
| 383 |
+
for v in result.violations:
|
| 384 |
+
violations.append(LintViolation(
|
| 385 |
+
line_no=v.line_no,
|
| 386 |
+
line_pos=v.line_pos,
|
| 387 |
+
code=v.rule_code(),
|
| 388 |
+
description=v.desc(),
|
| 389 |
+
name=v.rule_code(),
|
| 390 |
+
warning=getattr(v, "warning", False),
|
| 391 |
+
fixable=getattr(v, "fixable", False),
|
| 392 |
+
))
|
| 393 |
+
stats = {
|
| 394 |
+
"total": len(violations),
|
| 395 |
+
"errors": sum(1 for v in violations if not v.warning),
|
| 396 |
+
"warnings": sum(1 for v in violations if v.warning),
|
| 397 |
+
"fixable": sum(1 for v in violations if v.fixable),
|
| 398 |
+
}
|
| 399 |
+
return LintResponse(
|
| 400 |
+
dialect=dialect,
|
| 401 |
+
violations=violations,
|
| 402 |
+
passed=len(violations) == 0,
|
| 403 |
+
stats=stats,
|
| 404 |
+
)
|
| 405 |
+
except Exception as e:
|
| 406 |
+
raise HTTPException(status_code=500, detail=f"Lint error: {str(e)}\n{traceback.format_exc()}")
|
| 407 |
+
|
| 408 |
+
@app.post("/api/parse", response_model=ParseResponse, tags=["Analysis"])
|
| 409 |
+
def parse_sql(req: SqlRequest):
|
| 410 |
+
"""
|
| 411 |
+
Parse SQL into a full Abstract Syntax Tree (AST).
|
| 412 |
+
|
| 413 |
+
Returns a recursive tree of nodes with type, name, raw token value,
|
| 414 |
+
position info, and child nodes.
|
| 415 |
+
"""
|
| 416 |
+
try:
|
| 417 |
+
dialect = req.dialect if req.dialect in SQLFLUFF_DIALECTS else "ansi"
|
| 418 |
+
linter = Linter(dialect=dialect)
|
| 419 |
+
parsed = linter.parse_string(req.sql)
|
| 420 |
+
_reset_counter()
|
| 421 |
+
tree = _segment_to_node(parsed.tree)
|
| 422 |
+
return ParseResponse(
|
| 423 |
+
dialect=dialect,
|
| 424 |
+
tree=tree,
|
| 425 |
+
token_count=_count_tokens(tree),
|
| 426 |
+
depth=_tree_depth(tree),
|
| 427 |
+
)
|
| 428 |
+
except Exception as e:
|
| 429 |
+
raise HTTPException(status_code=500, detail=f"Parse error: {str(e)}\n{traceback.format_exc()}")
|
| 430 |
+
|
| 431 |
+
@app.post("/api/format", response_model=FormatResponse, tags=["Analysis"])
|
| 432 |
+
def format_sql(req: SqlRequest):
|
| 433 |
+
"""
|
| 434 |
+
Format and auto-fix SQL using SQLFluff.
|
| 435 |
+
|
| 436 |
+
Applies all auto-fixable rules and returns the cleaned SQL alongside
|
| 437 |
+
the original, with a count of fixes applied.
|
| 438 |
+
"""
|
| 439 |
+
try:
|
| 440 |
+
dialect = req.dialect if req.dialect in SQLFLUFF_DIALECTS else "ansi"
|
| 441 |
+
# Exclude CV10 (quoted literals convention) which crashes with
|
| 442 |
+
# "templated_file property is required" when fix() is called without
|
| 443 |
+
# a full templated context (known SQLFluff 4.x bug).
|
| 444 |
+
linter = Linter(dialect=dialect, exclude_rules=["CV10"])
|
| 445 |
+
lint_before = linter.lint_string(req.sql)
|
| 446 |
+
before_count = len(lint_before.violations)
|
| 447 |
+
parsed = linter.parse_string(req.sql)
|
| 448 |
+
fixed_tree, _ = linter.fix(parsed.tree)
|
| 449 |
+
formatted = fixed_tree.raw.strip() if fixed_tree else req.sql
|
| 450 |
+
lint_after = linter.lint_string(formatted)
|
| 451 |
+
after_count = len(lint_after.violations)
|
| 452 |
+
fixes_applied = max(0, before_count - after_count)
|
| 453 |
+
return FormatResponse(
|
| 454 |
+
dialect=dialect,
|
| 455 |
+
original=req.sql,
|
| 456 |
+
formatted=formatted,
|
| 457 |
+
changed=formatted != req.sql,
|
| 458 |
+
fixes_applied=fixes_applied,
|
| 459 |
+
)
|
| 460 |
+
except Exception as e:
|
| 461 |
+
raise HTTPException(status_code=500, detail=f"Format error: {str(e)}\n{traceback.format_exc()}")
|
| 462 |
+
|
| 463 |
+
@app.post("/api/inject", response_model=InjectionResponse, tags=["Security"])
|
| 464 |
+
def detect_injection(req: SqlRequest):
|
| 465 |
+
"""
|
| 466 |
+
Detect SQL injection patterns in the provided SQL string.
|
| 467 |
+
|
| 468 |
+
Checks for tautologies, stacked queries, comment-based bypasses,
|
| 469 |
+
UNION-based injection, time-based blind injection, command execution,
|
| 470 |
+
destructive statements, hex obfuscation, and schema reconnaissance.
|
| 471 |
+
"""
|
| 472 |
+
try:
|
| 473 |
+
patterns = _detect_injection(req.sql)
|
| 474 |
+
seen: set[str] = set()
|
| 475 |
+
unique_patterns: list[InjectionPattern] = []
|
| 476 |
+
for p in patterns:
|
| 477 |
+
if p.pattern_id not in seen:
|
| 478 |
+
seen.add(p.pattern_id)
|
| 479 |
+
unique_patterns.append(p)
|
| 480 |
+
|
| 481 |
+
score = min(100, sum(RISK_SCORE.get(p.risk_level, 0) for p in unique_patterns))
|
| 482 |
+
|
| 483 |
+
if score == 0:
|
| 484 |
+
summary = "No injection patterns detected. The SQL appears safe."
|
| 485 |
+
elif score < 25:
|
| 486 |
+
summary = f"Low risk ({score}/100): Minor obfuscation or reconnaissance patterns found."
|
| 487 |
+
elif score < 50:
|
| 488 |
+
summary = f"Medium risk ({score}/100): Suspicious patterns detected. Review carefully."
|
| 489 |
+
elif score < 75:
|
| 490 |
+
summary = f"High risk ({score}/100): Multiple injection indicators found. Do not execute."
|
| 491 |
+
else:
|
| 492 |
+
summary = f"Critical risk ({score}/100): Severe injection patterns detected. This SQL is dangerous."
|
| 493 |
+
|
| 494 |
+
return InjectionResponse(
|
| 495 |
+
dialect=req.dialect,
|
| 496 |
+
safe=score == 0,
|
| 497 |
+
risk_score=score,
|
| 498 |
+
patterns=unique_patterns,
|
| 499 |
+
summary=summary,
|
| 500 |
+
)
|
| 501 |
+
except Exception as e:
|
| 502 |
+
raise HTTPException(status_code=500, detail=f"Injection detection error: {str(e)}")
|
| 503 |
+
|
| 504 |
+
# ---------------------------------------------------------------------------
|
| 505 |
+
# Static file serving — React SPA
|
| 506 |
+
# ---------------------------------------------------------------------------
|
| 507 |
+
# The React build output is placed in ./static/ (relative to this file).
|
| 508 |
+
# FastAPI serves it at the root, with a catch-all that returns index.html
|
| 509 |
+
# for any unknown path (client-side routing).
|
| 510 |
+
|
| 511 |
+
STATIC_DIR = Path(__file__).parent / "static"
|
| 512 |
+
|
| 513 |
+
if STATIC_DIR.exists():
|
| 514 |
+
# Mount assets (JS/CSS/fonts) under /assets so they don't clash with /api
|
| 515 |
+
assets_dir = STATIC_DIR / "assets"
|
| 516 |
+
if assets_dir.exists():
|
| 517 |
+
app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")
|
| 518 |
+
|
| 519 |
+
@app.get("/{full_path:path}", include_in_schema=False)
|
| 520 |
+
async def serve_spa(full_path: str, request: Request):
|
| 521 |
+
"""Serve the React SPA for all non-API routes."""
|
| 522 |
+
# Try exact file match first (favicon.ico, robots.txt, etc.)
|
| 523 |
+
candidate = STATIC_DIR / full_path
|
| 524 |
+
if candidate.exists() and candidate.is_file():
|
| 525 |
+
return FileResponse(str(candidate))
|
| 526 |
+
# Fall back to index.html for client-side routing
|
| 527 |
+
return FileResponse(str(STATIC_DIR / "index.html"))
|
| 528 |
+
else:
|
| 529 |
+
@app.get("/", include_in_schema=False)
|
| 530 |
+
async def no_static():
|
| 531 |
+
return JSONResponse({
|
| 532 |
+
"message": "SQL Analyzer API is running. Build the React frontend and place it in api/static/.",
|
| 533 |
+
"docs": "/docs",
|
| 534 |
+
"endpoints": ["/api/health", "/api/lint", "/api/parse", "/api/format", "/api/inject"],
|
| 535 |
+
})
|
| 536 |
+
|
| 537 |
+
# ---------------------------------------------------------------------------
|
| 538 |
+
# Entry point
|
| 539 |
+
# ---------------------------------------------------------------------------
|
| 540 |
+
|
| 541 |
+
if __name__ == "__main__":
|
| 542 |
+
import uvicorn
|
| 543 |
+
port = int(os.environ.get("PORT", os.environ.get("PYTHON_API_PORT", "7860")))
|
| 544 |
+
uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")
|
build.sh
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# build.sh — Build the React frontend and start the FastAPI server
|
| 3 |
+
# Usage: ./build.sh [--port 7860]
|
| 4 |
+
|
| 5 |
+
set -e
|
| 6 |
+
|
| 7 |
+
PORT=${2:-7860}
|
| 8 |
+
|
| 9 |
+
echo "=== SQL Analyzer Build Script ==="
|
| 10 |
+
echo ""
|
| 11 |
+
|
| 12 |
+
# 1. Install Python deps
|
| 13 |
+
echo "[1/3] Installing Python dependencies..."
|
| 14 |
+
pip install -r requirements.txt --quiet
|
| 15 |
+
|
| 16 |
+
# 2. Build the React frontend
|
| 17 |
+
echo "[2/3] Building React frontend..."
|
| 18 |
+
cd frontend
|
| 19 |
+
pnpm install --frozen-lockfile --silent
|
| 20 |
+
pnpm build --silent
|
| 21 |
+
cd ..
|
| 22 |
+
|
| 23 |
+
echo " → Built to api/static/"
|
| 24 |
+
|
| 25 |
+
# 3. Start the server
|
| 26 |
+
echo "[3/3] Starting FastAPI server on port $PORT..."
|
| 27 |
+
echo " → Open http://localhost:$PORT"
|
| 28 |
+
echo ""
|
| 29 |
+
cd api
|
| 30 |
+
exec uvicorn main:app --host 0.0.0.0 --port "$PORT"
|
frontend/index.html
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
| 6 |
+
<title>SQL Analyzer — Linter, AST Explorer & Injection Detector</title>
|
| 7 |
+
<meta name="description" content="Interactive SQL linter, AST explorer, formatter, and injection detector powered by SQLFluff." />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 9 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 10 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 11 |
+
</head>
|
| 12 |
+
<body>
|
| 13 |
+
<div id="root"></div>
|
| 14 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 15 |
+
</body>
|
| 16 |
+
</html>
|
frontend/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "sql-analyzer-frontend",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"type": "module",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "vite",
|
| 7 |
+
"build": "tsc --noEmit && vite build",
|
| 8 |
+
"preview": "vite preview"
|
| 9 |
+
},
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"@codemirror/autocomplete": "^6.20.1",
|
| 12 |
+
"@codemirror/commands": "^6.10.3",
|
| 13 |
+
"@codemirror/lang-sql": "^6.8.0",
|
| 14 |
+
"@codemirror/language": "^6.12.3",
|
| 15 |
+
"@codemirror/lint": "^6.9.5",
|
| 16 |
+
"@codemirror/state": "^6.5.2",
|
| 17 |
+
"@codemirror/theme-one-dark": "^6.1.2",
|
| 18 |
+
"@codemirror/view": "^6.36.3",
|
| 19 |
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
| 20 |
+
"@radix-ui/react-select": "^2.2.6",
|
| 21 |
+
"@radix-ui/react-separator": "^1.1.7",
|
| 22 |
+
"@radix-ui/react-tabs": "^1.1.13",
|
| 23 |
+
"@radix-ui/react-tooltip": "^1.2.8",
|
| 24 |
+
"class-variance-authority": "^0.7.1",
|
| 25 |
+
"clsx": "^2.1.1",
|
| 26 |
+
"codemirror": "^6.0.1",
|
| 27 |
+
"framer-motion": "^12.0.0",
|
| 28 |
+
"lucide-react": "^0.453.0",
|
| 29 |
+
"react": "^19.0.0",
|
| 30 |
+
"react-dom": "^19.0.0",
|
| 31 |
+
"react-resizable-panels": "^3.0.6",
|
| 32 |
+
"sonner": "^2.0.7",
|
| 33 |
+
"swagger-ui-dist": "^5.18.2",
|
| 34 |
+
"tailwind-merge": "^3.0.0",
|
| 35 |
+
"wouter": "^3.3.5"
|
| 36 |
+
},
|
| 37 |
+
"devDependencies": {
|
| 38 |
+
"@tailwindcss/vite": "^4.1.3",
|
| 39 |
+
"@types/react": "^19.0.0",
|
| 40 |
+
"@types/react-dom": "^19.0.0",
|
| 41 |
+
"@vitejs/plugin-react": "^4.3.4",
|
| 42 |
+
"tailwindcss": "^4.1.3",
|
| 43 |
+
"typescript": "^5.7.3",
|
| 44 |
+
"vite": "^6.3.5"
|
| 45 |
+
}
|
| 46 |
+
}
|
frontend/pnpm-lock.yaml
ADDED
|
@@ -0,0 +1,2631 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
lockfileVersion: '9.0'
|
| 2 |
+
|
| 3 |
+
settings:
|
| 4 |
+
autoInstallPeers: true
|
| 5 |
+
excludeLinksFromLockfile: false
|
| 6 |
+
|
| 7 |
+
importers:
|
| 8 |
+
|
| 9 |
+
.:
|
| 10 |
+
dependencies:
|
| 11 |
+
'@codemirror/autocomplete':
|
| 12 |
+
specifier: ^6.20.1
|
| 13 |
+
version: 6.20.1
|
| 14 |
+
'@codemirror/commands':
|
| 15 |
+
specifier: ^6.10.3
|
| 16 |
+
version: 6.10.3
|
| 17 |
+
'@codemirror/lang-sql':
|
| 18 |
+
specifier: ^6.8.0
|
| 19 |
+
version: 6.10.0
|
| 20 |
+
'@codemirror/language':
|
| 21 |
+
specifier: ^6.12.3
|
| 22 |
+
version: 6.12.3
|
| 23 |
+
'@codemirror/lint':
|
| 24 |
+
specifier: ^6.9.5
|
| 25 |
+
version: 6.9.5
|
| 26 |
+
'@codemirror/state':
|
| 27 |
+
specifier: ^6.5.2
|
| 28 |
+
version: 6.6.0
|
| 29 |
+
'@codemirror/theme-one-dark':
|
| 30 |
+
specifier: ^6.1.2
|
| 31 |
+
version: 6.1.3
|
| 32 |
+
'@codemirror/view':
|
| 33 |
+
specifier: ^6.36.3
|
| 34 |
+
version: 6.41.1
|
| 35 |
+
'@radix-ui/react-scroll-area':
|
| 36 |
+
specifier: ^1.2.10
|
| 37 |
+
version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 38 |
+
'@radix-ui/react-select':
|
| 39 |
+
specifier: ^2.2.6
|
| 40 |
+
version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 41 |
+
'@radix-ui/react-separator':
|
| 42 |
+
specifier: ^1.1.7
|
| 43 |
+
version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 44 |
+
'@radix-ui/react-tabs':
|
| 45 |
+
specifier: ^1.1.13
|
| 46 |
+
version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 47 |
+
'@radix-ui/react-tooltip':
|
| 48 |
+
specifier: ^1.2.8
|
| 49 |
+
version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 50 |
+
class-variance-authority:
|
| 51 |
+
specifier: ^0.7.1
|
| 52 |
+
version: 0.7.1
|
| 53 |
+
clsx:
|
| 54 |
+
specifier: ^2.1.1
|
| 55 |
+
version: 2.1.1
|
| 56 |
+
codemirror:
|
| 57 |
+
specifier: ^6.0.1
|
| 58 |
+
version: 6.0.2
|
| 59 |
+
framer-motion:
|
| 60 |
+
specifier: ^12.0.0
|
| 61 |
+
version: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 62 |
+
lucide-react:
|
| 63 |
+
specifier: ^0.453.0
|
| 64 |
+
version: 0.453.0(react@19.2.5)
|
| 65 |
+
react:
|
| 66 |
+
specifier: ^19.0.0
|
| 67 |
+
version: 19.2.5
|
| 68 |
+
react-dom:
|
| 69 |
+
specifier: ^19.0.0
|
| 70 |
+
version: 19.2.5(react@19.2.5)
|
| 71 |
+
react-resizable-panels:
|
| 72 |
+
specifier: ^3.0.6
|
| 73 |
+
version: 3.0.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 74 |
+
sonner:
|
| 75 |
+
specifier: ^2.0.7
|
| 76 |
+
version: 2.0.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 77 |
+
swagger-ui-dist:
|
| 78 |
+
specifier: ^5.18.2
|
| 79 |
+
version: 5.32.5
|
| 80 |
+
tailwind-merge:
|
| 81 |
+
specifier: ^3.0.0
|
| 82 |
+
version: 3.5.0
|
| 83 |
+
wouter:
|
| 84 |
+
specifier: ^3.3.5
|
| 85 |
+
version: 3.9.0(react@19.2.5)
|
| 86 |
+
devDependencies:
|
| 87 |
+
'@tailwindcss/vite':
|
| 88 |
+
specifier: ^4.1.3
|
| 89 |
+
version: 4.2.4(vite@6.4.2(jiti@2.6.1)(lightningcss@1.32.0))
|
| 90 |
+
'@types/react':
|
| 91 |
+
specifier: ^19.0.0
|
| 92 |
+
version: 19.2.14
|
| 93 |
+
'@types/react-dom':
|
| 94 |
+
specifier: ^19.0.0
|
| 95 |
+
version: 19.2.3(@types/react@19.2.14)
|
| 96 |
+
'@vitejs/plugin-react':
|
| 97 |
+
specifier: ^4.3.4
|
| 98 |
+
version: 4.7.0(vite@6.4.2(jiti@2.6.1)(lightningcss@1.32.0))
|
| 99 |
+
tailwindcss:
|
| 100 |
+
specifier: ^4.1.3
|
| 101 |
+
version: 4.2.4
|
| 102 |
+
typescript:
|
| 103 |
+
specifier: ^5.7.3
|
| 104 |
+
version: 5.9.3
|
| 105 |
+
vite:
|
| 106 |
+
specifier: ^6.3.5
|
| 107 |
+
version: 6.4.2(jiti@2.6.1)(lightningcss@1.32.0)
|
| 108 |
+
|
| 109 |
+
packages:
|
| 110 |
+
|
| 111 |
+
'@babel/code-frame@7.29.0':
|
| 112 |
+
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
|
| 113 |
+
engines: {node: '>=6.9.0'}
|
| 114 |
+
|
| 115 |
+
'@babel/compat-data@7.29.0':
|
| 116 |
+
resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
|
| 117 |
+
engines: {node: '>=6.9.0'}
|
| 118 |
+
|
| 119 |
+
'@babel/core@7.29.0':
|
| 120 |
+
resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
|
| 121 |
+
engines: {node: '>=6.9.0'}
|
| 122 |
+
|
| 123 |
+
'@babel/generator@7.29.1':
|
| 124 |
+
resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
|
| 125 |
+
engines: {node: '>=6.9.0'}
|
| 126 |
+
|
| 127 |
+
'@babel/helper-compilation-targets@7.28.6':
|
| 128 |
+
resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
|
| 129 |
+
engines: {node: '>=6.9.0'}
|
| 130 |
+
|
| 131 |
+
'@babel/helper-globals@7.28.0':
|
| 132 |
+
resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
|
| 133 |
+
engines: {node: '>=6.9.0'}
|
| 134 |
+
|
| 135 |
+
'@babel/helper-module-imports@7.28.6':
|
| 136 |
+
resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
|
| 137 |
+
engines: {node: '>=6.9.0'}
|
| 138 |
+
|
| 139 |
+
'@babel/helper-module-transforms@7.28.6':
|
| 140 |
+
resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
|
| 141 |
+
engines: {node: '>=6.9.0'}
|
| 142 |
+
peerDependencies:
|
| 143 |
+
'@babel/core': ^7.0.0
|
| 144 |
+
|
| 145 |
+
'@babel/helper-plugin-utils@7.28.6':
|
| 146 |
+
resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
|
| 147 |
+
engines: {node: '>=6.9.0'}
|
| 148 |
+
|
| 149 |
+
'@babel/helper-string-parser@7.27.1':
|
| 150 |
+
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
| 151 |
+
engines: {node: '>=6.9.0'}
|
| 152 |
+
|
| 153 |
+
'@babel/helper-validator-identifier@7.28.5':
|
| 154 |
+
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
|
| 155 |
+
engines: {node: '>=6.9.0'}
|
| 156 |
+
|
| 157 |
+
'@babel/helper-validator-option@7.27.1':
|
| 158 |
+
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
|
| 159 |
+
engines: {node: '>=6.9.0'}
|
| 160 |
+
|
| 161 |
+
'@babel/helpers@7.29.2':
|
| 162 |
+
resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==}
|
| 163 |
+
engines: {node: '>=6.9.0'}
|
| 164 |
+
|
| 165 |
+
'@babel/parser@7.29.2':
|
| 166 |
+
resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==}
|
| 167 |
+
engines: {node: '>=6.0.0'}
|
| 168 |
+
hasBin: true
|
| 169 |
+
|
| 170 |
+
'@babel/plugin-transform-react-jsx-self@7.27.1':
|
| 171 |
+
resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
|
| 172 |
+
engines: {node: '>=6.9.0'}
|
| 173 |
+
peerDependencies:
|
| 174 |
+
'@babel/core': ^7.0.0-0
|
| 175 |
+
|
| 176 |
+
'@babel/plugin-transform-react-jsx-source@7.27.1':
|
| 177 |
+
resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
|
| 178 |
+
engines: {node: '>=6.9.0'}
|
| 179 |
+
peerDependencies:
|
| 180 |
+
'@babel/core': ^7.0.0-0
|
| 181 |
+
|
| 182 |
+
'@babel/template@7.28.6':
|
| 183 |
+
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
|
| 184 |
+
engines: {node: '>=6.9.0'}
|
| 185 |
+
|
| 186 |
+
'@babel/traverse@7.29.0':
|
| 187 |
+
resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
|
| 188 |
+
engines: {node: '>=6.9.0'}
|
| 189 |
+
|
| 190 |
+
'@babel/types@7.29.0':
|
| 191 |
+
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
| 192 |
+
engines: {node: '>=6.9.0'}
|
| 193 |
+
|
| 194 |
+
'@codemirror/autocomplete@6.20.1':
|
| 195 |
+
resolution: {integrity: sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==}
|
| 196 |
+
|
| 197 |
+
'@codemirror/commands@6.10.3':
|
| 198 |
+
resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==}
|
| 199 |
+
|
| 200 |
+
'@codemirror/lang-sql@6.10.0':
|
| 201 |
+
resolution: {integrity: sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==}
|
| 202 |
+
|
| 203 |
+
'@codemirror/language@6.12.3':
|
| 204 |
+
resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==}
|
| 205 |
+
|
| 206 |
+
'@codemirror/lint@6.9.5':
|
| 207 |
+
resolution: {integrity: sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==}
|
| 208 |
+
|
| 209 |
+
'@codemirror/search@6.7.0':
|
| 210 |
+
resolution: {integrity: sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==}
|
| 211 |
+
|
| 212 |
+
'@codemirror/state@6.6.0':
|
| 213 |
+
resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==}
|
| 214 |
+
|
| 215 |
+
'@codemirror/theme-one-dark@6.1.3':
|
| 216 |
+
resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
|
| 217 |
+
|
| 218 |
+
'@codemirror/view@6.41.1':
|
| 219 |
+
resolution: {integrity: sha512-ToDnWKbBnke+ZLrP6vgTTDScGi5H37YYuZGniQaBzxMVdtCxMrslsmtnOvbPZk4RX9bvkQqnWR/WS/35tJA0qg==}
|
| 220 |
+
|
| 221 |
+
'@esbuild/aix-ppc64@0.25.12':
|
| 222 |
+
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
| 223 |
+
engines: {node: '>=18'}
|
| 224 |
+
cpu: [ppc64]
|
| 225 |
+
os: [aix]
|
| 226 |
+
|
| 227 |
+
'@esbuild/android-arm64@0.25.12':
|
| 228 |
+
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
| 229 |
+
engines: {node: '>=18'}
|
| 230 |
+
cpu: [arm64]
|
| 231 |
+
os: [android]
|
| 232 |
+
|
| 233 |
+
'@esbuild/android-arm@0.25.12':
|
| 234 |
+
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
| 235 |
+
engines: {node: '>=18'}
|
| 236 |
+
cpu: [arm]
|
| 237 |
+
os: [android]
|
| 238 |
+
|
| 239 |
+
'@esbuild/android-x64@0.25.12':
|
| 240 |
+
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
| 241 |
+
engines: {node: '>=18'}
|
| 242 |
+
cpu: [x64]
|
| 243 |
+
os: [android]
|
| 244 |
+
|
| 245 |
+
'@esbuild/darwin-arm64@0.25.12':
|
| 246 |
+
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
| 247 |
+
engines: {node: '>=18'}
|
| 248 |
+
cpu: [arm64]
|
| 249 |
+
os: [darwin]
|
| 250 |
+
|
| 251 |
+
'@esbuild/darwin-x64@0.25.12':
|
| 252 |
+
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
| 253 |
+
engines: {node: '>=18'}
|
| 254 |
+
cpu: [x64]
|
| 255 |
+
os: [darwin]
|
| 256 |
+
|
| 257 |
+
'@esbuild/freebsd-arm64@0.25.12':
|
| 258 |
+
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
| 259 |
+
engines: {node: '>=18'}
|
| 260 |
+
cpu: [arm64]
|
| 261 |
+
os: [freebsd]
|
| 262 |
+
|
| 263 |
+
'@esbuild/freebsd-x64@0.25.12':
|
| 264 |
+
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
| 265 |
+
engines: {node: '>=18'}
|
| 266 |
+
cpu: [x64]
|
| 267 |
+
os: [freebsd]
|
| 268 |
+
|
| 269 |
+
'@esbuild/linux-arm64@0.25.12':
|
| 270 |
+
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
| 271 |
+
engines: {node: '>=18'}
|
| 272 |
+
cpu: [arm64]
|
| 273 |
+
os: [linux]
|
| 274 |
+
|
| 275 |
+
'@esbuild/linux-arm@0.25.12':
|
| 276 |
+
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
| 277 |
+
engines: {node: '>=18'}
|
| 278 |
+
cpu: [arm]
|
| 279 |
+
os: [linux]
|
| 280 |
+
|
| 281 |
+
'@esbuild/linux-ia32@0.25.12':
|
| 282 |
+
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
| 283 |
+
engines: {node: '>=18'}
|
| 284 |
+
cpu: [ia32]
|
| 285 |
+
os: [linux]
|
| 286 |
+
|
| 287 |
+
'@esbuild/linux-loong64@0.25.12':
|
| 288 |
+
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
| 289 |
+
engines: {node: '>=18'}
|
| 290 |
+
cpu: [loong64]
|
| 291 |
+
os: [linux]
|
| 292 |
+
|
| 293 |
+
'@esbuild/linux-mips64el@0.25.12':
|
| 294 |
+
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
| 295 |
+
engines: {node: '>=18'}
|
| 296 |
+
cpu: [mips64el]
|
| 297 |
+
os: [linux]
|
| 298 |
+
|
| 299 |
+
'@esbuild/linux-ppc64@0.25.12':
|
| 300 |
+
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
| 301 |
+
engines: {node: '>=18'}
|
| 302 |
+
cpu: [ppc64]
|
| 303 |
+
os: [linux]
|
| 304 |
+
|
| 305 |
+
'@esbuild/linux-riscv64@0.25.12':
|
| 306 |
+
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
| 307 |
+
engines: {node: '>=18'}
|
| 308 |
+
cpu: [riscv64]
|
| 309 |
+
os: [linux]
|
| 310 |
+
|
| 311 |
+
'@esbuild/linux-s390x@0.25.12':
|
| 312 |
+
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
| 313 |
+
engines: {node: '>=18'}
|
| 314 |
+
cpu: [s390x]
|
| 315 |
+
os: [linux]
|
| 316 |
+
|
| 317 |
+
'@esbuild/linux-x64@0.25.12':
|
| 318 |
+
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
| 319 |
+
engines: {node: '>=18'}
|
| 320 |
+
cpu: [x64]
|
| 321 |
+
os: [linux]
|
| 322 |
+
|
| 323 |
+
'@esbuild/netbsd-arm64@0.25.12':
|
| 324 |
+
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
| 325 |
+
engines: {node: '>=18'}
|
| 326 |
+
cpu: [arm64]
|
| 327 |
+
os: [netbsd]
|
| 328 |
+
|
| 329 |
+
'@esbuild/netbsd-x64@0.25.12':
|
| 330 |
+
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
| 331 |
+
engines: {node: '>=18'}
|
| 332 |
+
cpu: [x64]
|
| 333 |
+
os: [netbsd]
|
| 334 |
+
|
| 335 |
+
'@esbuild/openbsd-arm64@0.25.12':
|
| 336 |
+
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
| 337 |
+
engines: {node: '>=18'}
|
| 338 |
+
cpu: [arm64]
|
| 339 |
+
os: [openbsd]
|
| 340 |
+
|
| 341 |
+
'@esbuild/openbsd-x64@0.25.12':
|
| 342 |
+
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
| 343 |
+
engines: {node: '>=18'}
|
| 344 |
+
cpu: [x64]
|
| 345 |
+
os: [openbsd]
|
| 346 |
+
|
| 347 |
+
'@esbuild/openharmony-arm64@0.25.12':
|
| 348 |
+
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
| 349 |
+
engines: {node: '>=18'}
|
| 350 |
+
cpu: [arm64]
|
| 351 |
+
os: [openharmony]
|
| 352 |
+
|
| 353 |
+
'@esbuild/sunos-x64@0.25.12':
|
| 354 |
+
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
| 355 |
+
engines: {node: '>=18'}
|
| 356 |
+
cpu: [x64]
|
| 357 |
+
os: [sunos]
|
| 358 |
+
|
| 359 |
+
'@esbuild/win32-arm64@0.25.12':
|
| 360 |
+
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
| 361 |
+
engines: {node: '>=18'}
|
| 362 |
+
cpu: [arm64]
|
| 363 |
+
os: [win32]
|
| 364 |
+
|
| 365 |
+
'@esbuild/win32-ia32@0.25.12':
|
| 366 |
+
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
| 367 |
+
engines: {node: '>=18'}
|
| 368 |
+
cpu: [ia32]
|
| 369 |
+
os: [win32]
|
| 370 |
+
|
| 371 |
+
'@esbuild/win32-x64@0.25.12':
|
| 372 |
+
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
| 373 |
+
engines: {node: '>=18'}
|
| 374 |
+
cpu: [x64]
|
| 375 |
+
os: [win32]
|
| 376 |
+
|
| 377 |
+
'@floating-ui/core@1.7.5':
|
| 378 |
+
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
|
| 379 |
+
|
| 380 |
+
'@floating-ui/dom@1.7.6':
|
| 381 |
+
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
|
| 382 |
+
|
| 383 |
+
'@floating-ui/react-dom@2.1.8':
|
| 384 |
+
resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
|
| 385 |
+
peerDependencies:
|
| 386 |
+
react: '>=16.8.0'
|
| 387 |
+
react-dom: '>=16.8.0'
|
| 388 |
+
|
| 389 |
+
'@floating-ui/utils@0.2.11':
|
| 390 |
+
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
|
| 391 |
+
|
| 392 |
+
'@jridgewell/gen-mapping@0.3.13':
|
| 393 |
+
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
| 394 |
+
|
| 395 |
+
'@jridgewell/remapping@2.3.5':
|
| 396 |
+
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
|
| 397 |
+
|
| 398 |
+
'@jridgewell/resolve-uri@3.1.2':
|
| 399 |
+
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
| 400 |
+
engines: {node: '>=6.0.0'}
|
| 401 |
+
|
| 402 |
+
'@jridgewell/sourcemap-codec@1.5.5':
|
| 403 |
+
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
| 404 |
+
|
| 405 |
+
'@jridgewell/trace-mapping@0.3.31':
|
| 406 |
+
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
| 407 |
+
|
| 408 |
+
'@lezer/common@1.5.2':
|
| 409 |
+
resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
|
| 410 |
+
|
| 411 |
+
'@lezer/highlight@1.2.3':
|
| 412 |
+
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
|
| 413 |
+
|
| 414 |
+
'@lezer/lr@1.4.10':
|
| 415 |
+
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
|
| 416 |
+
|
| 417 |
+
'@marijn/find-cluster-break@1.0.2':
|
| 418 |
+
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
|
| 419 |
+
|
| 420 |
+
'@radix-ui/number@1.1.1':
|
| 421 |
+
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
| 422 |
+
|
| 423 |
+
'@radix-ui/primitive@1.1.3':
|
| 424 |
+
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
| 425 |
+
|
| 426 |
+
'@radix-ui/react-arrow@1.1.7':
|
| 427 |
+
resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
|
| 428 |
+
peerDependencies:
|
| 429 |
+
'@types/react': '*'
|
| 430 |
+
'@types/react-dom': '*'
|
| 431 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 432 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 433 |
+
peerDependenciesMeta:
|
| 434 |
+
'@types/react':
|
| 435 |
+
optional: true
|
| 436 |
+
'@types/react-dom':
|
| 437 |
+
optional: true
|
| 438 |
+
|
| 439 |
+
'@radix-ui/react-collection@1.1.7':
|
| 440 |
+
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
|
| 441 |
+
peerDependencies:
|
| 442 |
+
'@types/react': '*'
|
| 443 |
+
'@types/react-dom': '*'
|
| 444 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 445 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 446 |
+
peerDependenciesMeta:
|
| 447 |
+
'@types/react':
|
| 448 |
+
optional: true
|
| 449 |
+
'@types/react-dom':
|
| 450 |
+
optional: true
|
| 451 |
+
|
| 452 |
+
'@radix-ui/react-compose-refs@1.1.2':
|
| 453 |
+
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
|
| 454 |
+
peerDependencies:
|
| 455 |
+
'@types/react': '*'
|
| 456 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 457 |
+
peerDependenciesMeta:
|
| 458 |
+
'@types/react':
|
| 459 |
+
optional: true
|
| 460 |
+
|
| 461 |
+
'@radix-ui/react-context@1.1.2':
|
| 462 |
+
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
|
| 463 |
+
peerDependencies:
|
| 464 |
+
'@types/react': '*'
|
| 465 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 466 |
+
peerDependenciesMeta:
|
| 467 |
+
'@types/react':
|
| 468 |
+
optional: true
|
| 469 |
+
|
| 470 |
+
'@radix-ui/react-direction@1.1.1':
|
| 471 |
+
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
|
| 472 |
+
peerDependencies:
|
| 473 |
+
'@types/react': '*'
|
| 474 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 475 |
+
peerDependenciesMeta:
|
| 476 |
+
'@types/react':
|
| 477 |
+
optional: true
|
| 478 |
+
|
| 479 |
+
'@radix-ui/react-dismissable-layer@1.1.11':
|
| 480 |
+
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
|
| 481 |
+
peerDependencies:
|
| 482 |
+
'@types/react': '*'
|
| 483 |
+
'@types/react-dom': '*'
|
| 484 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 485 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 486 |
+
peerDependenciesMeta:
|
| 487 |
+
'@types/react':
|
| 488 |
+
optional: true
|
| 489 |
+
'@types/react-dom':
|
| 490 |
+
optional: true
|
| 491 |
+
|
| 492 |
+
'@radix-ui/react-focus-guards@1.1.3':
|
| 493 |
+
resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
|
| 494 |
+
peerDependencies:
|
| 495 |
+
'@types/react': '*'
|
| 496 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 497 |
+
peerDependenciesMeta:
|
| 498 |
+
'@types/react':
|
| 499 |
+
optional: true
|
| 500 |
+
|
| 501 |
+
'@radix-ui/react-focus-scope@1.1.7':
|
| 502 |
+
resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
|
| 503 |
+
peerDependencies:
|
| 504 |
+
'@types/react': '*'
|
| 505 |
+
'@types/react-dom': '*'
|
| 506 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 507 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 508 |
+
peerDependenciesMeta:
|
| 509 |
+
'@types/react':
|
| 510 |
+
optional: true
|
| 511 |
+
'@types/react-dom':
|
| 512 |
+
optional: true
|
| 513 |
+
|
| 514 |
+
'@radix-ui/react-id@1.1.1':
|
| 515 |
+
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
|
| 516 |
+
peerDependencies:
|
| 517 |
+
'@types/react': '*'
|
| 518 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 519 |
+
peerDependenciesMeta:
|
| 520 |
+
'@types/react':
|
| 521 |
+
optional: true
|
| 522 |
+
|
| 523 |
+
'@radix-ui/react-popper@1.2.8':
|
| 524 |
+
resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
|
| 525 |
+
peerDependencies:
|
| 526 |
+
'@types/react': '*'
|
| 527 |
+
'@types/react-dom': '*'
|
| 528 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 529 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 530 |
+
peerDependenciesMeta:
|
| 531 |
+
'@types/react':
|
| 532 |
+
optional: true
|
| 533 |
+
'@types/react-dom':
|
| 534 |
+
optional: true
|
| 535 |
+
|
| 536 |
+
'@radix-ui/react-portal@1.1.9':
|
| 537 |
+
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
|
| 538 |
+
peerDependencies:
|
| 539 |
+
'@types/react': '*'
|
| 540 |
+
'@types/react-dom': '*'
|
| 541 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 542 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 543 |
+
peerDependenciesMeta:
|
| 544 |
+
'@types/react':
|
| 545 |
+
optional: true
|
| 546 |
+
'@types/react-dom':
|
| 547 |
+
optional: true
|
| 548 |
+
|
| 549 |
+
'@radix-ui/react-presence@1.1.5':
|
| 550 |
+
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
|
| 551 |
+
peerDependencies:
|
| 552 |
+
'@types/react': '*'
|
| 553 |
+
'@types/react-dom': '*'
|
| 554 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 555 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 556 |
+
peerDependenciesMeta:
|
| 557 |
+
'@types/react':
|
| 558 |
+
optional: true
|
| 559 |
+
'@types/react-dom':
|
| 560 |
+
optional: true
|
| 561 |
+
|
| 562 |
+
'@radix-ui/react-primitive@2.1.3':
|
| 563 |
+
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
|
| 564 |
+
peerDependencies:
|
| 565 |
+
'@types/react': '*'
|
| 566 |
+
'@types/react-dom': '*'
|
| 567 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 568 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 569 |
+
peerDependenciesMeta:
|
| 570 |
+
'@types/react':
|
| 571 |
+
optional: true
|
| 572 |
+
'@types/react-dom':
|
| 573 |
+
optional: true
|
| 574 |
+
|
| 575 |
+
'@radix-ui/react-primitive@2.1.4':
|
| 576 |
+
resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
|
| 577 |
+
peerDependencies:
|
| 578 |
+
'@types/react': '*'
|
| 579 |
+
'@types/react-dom': '*'
|
| 580 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 581 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 582 |
+
peerDependenciesMeta:
|
| 583 |
+
'@types/react':
|
| 584 |
+
optional: true
|
| 585 |
+
'@types/react-dom':
|
| 586 |
+
optional: true
|
| 587 |
+
|
| 588 |
+
'@radix-ui/react-roving-focus@1.1.11':
|
| 589 |
+
resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
|
| 590 |
+
peerDependencies:
|
| 591 |
+
'@types/react': '*'
|
| 592 |
+
'@types/react-dom': '*'
|
| 593 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 594 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 595 |
+
peerDependenciesMeta:
|
| 596 |
+
'@types/react':
|
| 597 |
+
optional: true
|
| 598 |
+
'@types/react-dom':
|
| 599 |
+
optional: true
|
| 600 |
+
|
| 601 |
+
'@radix-ui/react-scroll-area@1.2.10':
|
| 602 |
+
resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==}
|
| 603 |
+
peerDependencies:
|
| 604 |
+
'@types/react': '*'
|
| 605 |
+
'@types/react-dom': '*'
|
| 606 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 607 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 608 |
+
peerDependenciesMeta:
|
| 609 |
+
'@types/react':
|
| 610 |
+
optional: true
|
| 611 |
+
'@types/react-dom':
|
| 612 |
+
optional: true
|
| 613 |
+
|
| 614 |
+
'@radix-ui/react-select@2.2.6':
|
| 615 |
+
resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
|
| 616 |
+
peerDependencies:
|
| 617 |
+
'@types/react': '*'
|
| 618 |
+
'@types/react-dom': '*'
|
| 619 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 620 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 621 |
+
peerDependenciesMeta:
|
| 622 |
+
'@types/react':
|
| 623 |
+
optional: true
|
| 624 |
+
'@types/react-dom':
|
| 625 |
+
optional: true
|
| 626 |
+
|
| 627 |
+
'@radix-ui/react-separator@1.1.8':
|
| 628 |
+
resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==}
|
| 629 |
+
peerDependencies:
|
| 630 |
+
'@types/react': '*'
|
| 631 |
+
'@types/react-dom': '*'
|
| 632 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 633 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 634 |
+
peerDependenciesMeta:
|
| 635 |
+
'@types/react':
|
| 636 |
+
optional: true
|
| 637 |
+
'@types/react-dom':
|
| 638 |
+
optional: true
|
| 639 |
+
|
| 640 |
+
'@radix-ui/react-slot@1.2.3':
|
| 641 |
+
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
|
| 642 |
+
peerDependencies:
|
| 643 |
+
'@types/react': '*'
|
| 644 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 645 |
+
peerDependenciesMeta:
|
| 646 |
+
'@types/react':
|
| 647 |
+
optional: true
|
| 648 |
+
|
| 649 |
+
'@radix-ui/react-slot@1.2.4':
|
| 650 |
+
resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
|
| 651 |
+
peerDependencies:
|
| 652 |
+
'@types/react': '*'
|
| 653 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 654 |
+
peerDependenciesMeta:
|
| 655 |
+
'@types/react':
|
| 656 |
+
optional: true
|
| 657 |
+
|
| 658 |
+
'@radix-ui/react-tabs@1.1.13':
|
| 659 |
+
resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
|
| 660 |
+
peerDependencies:
|
| 661 |
+
'@types/react': '*'
|
| 662 |
+
'@types/react-dom': '*'
|
| 663 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 664 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 665 |
+
peerDependenciesMeta:
|
| 666 |
+
'@types/react':
|
| 667 |
+
optional: true
|
| 668 |
+
'@types/react-dom':
|
| 669 |
+
optional: true
|
| 670 |
+
|
| 671 |
+
'@radix-ui/react-tooltip@1.2.8':
|
| 672 |
+
resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
|
| 673 |
+
peerDependencies:
|
| 674 |
+
'@types/react': '*'
|
| 675 |
+
'@types/react-dom': '*'
|
| 676 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 677 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 678 |
+
peerDependenciesMeta:
|
| 679 |
+
'@types/react':
|
| 680 |
+
optional: true
|
| 681 |
+
'@types/react-dom':
|
| 682 |
+
optional: true
|
| 683 |
+
|
| 684 |
+
'@radix-ui/react-use-callback-ref@1.1.1':
|
| 685 |
+
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
|
| 686 |
+
peerDependencies:
|
| 687 |
+
'@types/react': '*'
|
| 688 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 689 |
+
peerDependenciesMeta:
|
| 690 |
+
'@types/react':
|
| 691 |
+
optional: true
|
| 692 |
+
|
| 693 |
+
'@radix-ui/react-use-controllable-state@1.2.2':
|
| 694 |
+
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
| 695 |
+
peerDependencies:
|
| 696 |
+
'@types/react': '*'
|
| 697 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 698 |
+
peerDependenciesMeta:
|
| 699 |
+
'@types/react':
|
| 700 |
+
optional: true
|
| 701 |
+
|
| 702 |
+
'@radix-ui/react-use-effect-event@0.0.2':
|
| 703 |
+
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
|
| 704 |
+
peerDependencies:
|
| 705 |
+
'@types/react': '*'
|
| 706 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 707 |
+
peerDependenciesMeta:
|
| 708 |
+
'@types/react':
|
| 709 |
+
optional: true
|
| 710 |
+
|
| 711 |
+
'@radix-ui/react-use-escape-keydown@1.1.1':
|
| 712 |
+
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
|
| 713 |
+
peerDependencies:
|
| 714 |
+
'@types/react': '*'
|
| 715 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 716 |
+
peerDependenciesMeta:
|
| 717 |
+
'@types/react':
|
| 718 |
+
optional: true
|
| 719 |
+
|
| 720 |
+
'@radix-ui/react-use-layout-effect@1.1.1':
|
| 721 |
+
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
|
| 722 |
+
peerDependencies:
|
| 723 |
+
'@types/react': '*'
|
| 724 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 725 |
+
peerDependenciesMeta:
|
| 726 |
+
'@types/react':
|
| 727 |
+
optional: true
|
| 728 |
+
|
| 729 |
+
'@radix-ui/react-use-previous@1.1.1':
|
| 730 |
+
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
|
| 731 |
+
peerDependencies:
|
| 732 |
+
'@types/react': '*'
|
| 733 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 734 |
+
peerDependenciesMeta:
|
| 735 |
+
'@types/react':
|
| 736 |
+
optional: true
|
| 737 |
+
|
| 738 |
+
'@radix-ui/react-use-rect@1.1.1':
|
| 739 |
+
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
|
| 740 |
+
peerDependencies:
|
| 741 |
+
'@types/react': '*'
|
| 742 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 743 |
+
peerDependenciesMeta:
|
| 744 |
+
'@types/react':
|
| 745 |
+
optional: true
|
| 746 |
+
|
| 747 |
+
'@radix-ui/react-use-size@1.1.1':
|
| 748 |
+
resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
|
| 749 |
+
peerDependencies:
|
| 750 |
+
'@types/react': '*'
|
| 751 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 752 |
+
peerDependenciesMeta:
|
| 753 |
+
'@types/react':
|
| 754 |
+
optional: true
|
| 755 |
+
|
| 756 |
+
'@radix-ui/react-visually-hidden@1.2.3':
|
| 757 |
+
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
|
| 758 |
+
peerDependencies:
|
| 759 |
+
'@types/react': '*'
|
| 760 |
+
'@types/react-dom': '*'
|
| 761 |
+
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 762 |
+
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
| 763 |
+
peerDependenciesMeta:
|
| 764 |
+
'@types/react':
|
| 765 |
+
optional: true
|
| 766 |
+
'@types/react-dom':
|
| 767 |
+
optional: true
|
| 768 |
+
|
| 769 |
+
'@radix-ui/rect@1.1.1':
|
| 770 |
+
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
|
| 771 |
+
|
| 772 |
+
'@rolldown/pluginutils@1.0.0-beta.27':
|
| 773 |
+
resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
|
| 774 |
+
|
| 775 |
+
'@rollup/rollup-android-arm-eabi@4.60.2':
|
| 776 |
+
resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==}
|
| 777 |
+
cpu: [arm]
|
| 778 |
+
os: [android]
|
| 779 |
+
|
| 780 |
+
'@rollup/rollup-android-arm64@4.60.2':
|
| 781 |
+
resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==}
|
| 782 |
+
cpu: [arm64]
|
| 783 |
+
os: [android]
|
| 784 |
+
|
| 785 |
+
'@rollup/rollup-darwin-arm64@4.60.2':
|
| 786 |
+
resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==}
|
| 787 |
+
cpu: [arm64]
|
| 788 |
+
os: [darwin]
|
| 789 |
+
|
| 790 |
+
'@rollup/rollup-darwin-x64@4.60.2':
|
| 791 |
+
resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==}
|
| 792 |
+
cpu: [x64]
|
| 793 |
+
os: [darwin]
|
| 794 |
+
|
| 795 |
+
'@rollup/rollup-freebsd-arm64@4.60.2':
|
| 796 |
+
resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==}
|
| 797 |
+
cpu: [arm64]
|
| 798 |
+
os: [freebsd]
|
| 799 |
+
|
| 800 |
+
'@rollup/rollup-freebsd-x64@4.60.2':
|
| 801 |
+
resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==}
|
| 802 |
+
cpu: [x64]
|
| 803 |
+
os: [freebsd]
|
| 804 |
+
|
| 805 |
+
'@rollup/rollup-linux-arm-gnueabihf@4.60.2':
|
| 806 |
+
resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==}
|
| 807 |
+
cpu: [arm]
|
| 808 |
+
os: [linux]
|
| 809 |
+
libc: [glibc]
|
| 810 |
+
|
| 811 |
+
'@rollup/rollup-linux-arm-musleabihf@4.60.2':
|
| 812 |
+
resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==}
|
| 813 |
+
cpu: [arm]
|
| 814 |
+
os: [linux]
|
| 815 |
+
libc: [musl]
|
| 816 |
+
|
| 817 |
+
'@rollup/rollup-linux-arm64-gnu@4.60.2':
|
| 818 |
+
resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==}
|
| 819 |
+
cpu: [arm64]
|
| 820 |
+
os: [linux]
|
| 821 |
+
libc: [glibc]
|
| 822 |
+
|
| 823 |
+
'@rollup/rollup-linux-arm64-musl@4.60.2':
|
| 824 |
+
resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==}
|
| 825 |
+
cpu: [arm64]
|
| 826 |
+
os: [linux]
|
| 827 |
+
libc: [musl]
|
| 828 |
+
|
| 829 |
+
'@rollup/rollup-linux-loong64-gnu@4.60.2':
|
| 830 |
+
resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==}
|
| 831 |
+
cpu: [loong64]
|
| 832 |
+
os: [linux]
|
| 833 |
+
libc: [glibc]
|
| 834 |
+
|
| 835 |
+
'@rollup/rollup-linux-loong64-musl@4.60.2':
|
| 836 |
+
resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==}
|
| 837 |
+
cpu: [loong64]
|
| 838 |
+
os: [linux]
|
| 839 |
+
libc: [musl]
|
| 840 |
+
|
| 841 |
+
'@rollup/rollup-linux-ppc64-gnu@4.60.2':
|
| 842 |
+
resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==}
|
| 843 |
+
cpu: [ppc64]
|
| 844 |
+
os: [linux]
|
| 845 |
+
libc: [glibc]
|
| 846 |
+
|
| 847 |
+
'@rollup/rollup-linux-ppc64-musl@4.60.2':
|
| 848 |
+
resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==}
|
| 849 |
+
cpu: [ppc64]
|
| 850 |
+
os: [linux]
|
| 851 |
+
libc: [musl]
|
| 852 |
+
|
| 853 |
+
'@rollup/rollup-linux-riscv64-gnu@4.60.2':
|
| 854 |
+
resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==}
|
| 855 |
+
cpu: [riscv64]
|
| 856 |
+
os: [linux]
|
| 857 |
+
libc: [glibc]
|
| 858 |
+
|
| 859 |
+
'@rollup/rollup-linux-riscv64-musl@4.60.2':
|
| 860 |
+
resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==}
|
| 861 |
+
cpu: [riscv64]
|
| 862 |
+
os: [linux]
|
| 863 |
+
libc: [musl]
|
| 864 |
+
|
| 865 |
+
'@rollup/rollup-linux-s390x-gnu@4.60.2':
|
| 866 |
+
resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==}
|
| 867 |
+
cpu: [s390x]
|
| 868 |
+
os: [linux]
|
| 869 |
+
libc: [glibc]
|
| 870 |
+
|
| 871 |
+
'@rollup/rollup-linux-x64-gnu@4.60.2':
|
| 872 |
+
resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==}
|
| 873 |
+
cpu: [x64]
|
| 874 |
+
os: [linux]
|
| 875 |
+
libc: [glibc]
|
| 876 |
+
|
| 877 |
+
'@rollup/rollup-linux-x64-musl@4.60.2':
|
| 878 |
+
resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==}
|
| 879 |
+
cpu: [x64]
|
| 880 |
+
os: [linux]
|
| 881 |
+
libc: [musl]
|
| 882 |
+
|
| 883 |
+
'@rollup/rollup-openbsd-x64@4.60.2':
|
| 884 |
+
resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==}
|
| 885 |
+
cpu: [x64]
|
| 886 |
+
os: [openbsd]
|
| 887 |
+
|
| 888 |
+
'@rollup/rollup-openharmony-arm64@4.60.2':
|
| 889 |
+
resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==}
|
| 890 |
+
cpu: [arm64]
|
| 891 |
+
os: [openharmony]
|
| 892 |
+
|
| 893 |
+
'@rollup/rollup-win32-arm64-msvc@4.60.2':
|
| 894 |
+
resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==}
|
| 895 |
+
cpu: [arm64]
|
| 896 |
+
os: [win32]
|
| 897 |
+
|
| 898 |
+
'@rollup/rollup-win32-ia32-msvc@4.60.2':
|
| 899 |
+
resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==}
|
| 900 |
+
cpu: [ia32]
|
| 901 |
+
os: [win32]
|
| 902 |
+
|
| 903 |
+
'@rollup/rollup-win32-x64-gnu@4.60.2':
|
| 904 |
+
resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==}
|
| 905 |
+
cpu: [x64]
|
| 906 |
+
os: [win32]
|
| 907 |
+
|
| 908 |
+
'@rollup/rollup-win32-x64-msvc@4.60.2':
|
| 909 |
+
resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==}
|
| 910 |
+
cpu: [x64]
|
| 911 |
+
os: [win32]
|
| 912 |
+
|
| 913 |
+
'@scarf/scarf@1.4.0':
|
| 914 |
+
resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
|
| 915 |
+
|
| 916 |
+
'@tailwindcss/node@4.2.4':
|
| 917 |
+
resolution: {integrity: sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==}
|
| 918 |
+
|
| 919 |
+
'@tailwindcss/oxide-android-arm64@4.2.4':
|
| 920 |
+
resolution: {integrity: sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==}
|
| 921 |
+
engines: {node: '>= 20'}
|
| 922 |
+
cpu: [arm64]
|
| 923 |
+
os: [android]
|
| 924 |
+
|
| 925 |
+
'@tailwindcss/oxide-darwin-arm64@4.2.4':
|
| 926 |
+
resolution: {integrity: sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==}
|
| 927 |
+
engines: {node: '>= 20'}
|
| 928 |
+
cpu: [arm64]
|
| 929 |
+
os: [darwin]
|
| 930 |
+
|
| 931 |
+
'@tailwindcss/oxide-darwin-x64@4.2.4':
|
| 932 |
+
resolution: {integrity: sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==}
|
| 933 |
+
engines: {node: '>= 20'}
|
| 934 |
+
cpu: [x64]
|
| 935 |
+
os: [darwin]
|
| 936 |
+
|
| 937 |
+
'@tailwindcss/oxide-freebsd-x64@4.2.4':
|
| 938 |
+
resolution: {integrity: sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==}
|
| 939 |
+
engines: {node: '>= 20'}
|
| 940 |
+
cpu: [x64]
|
| 941 |
+
os: [freebsd]
|
| 942 |
+
|
| 943 |
+
'@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4':
|
| 944 |
+
resolution: {integrity: sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==}
|
| 945 |
+
engines: {node: '>= 20'}
|
| 946 |
+
cpu: [arm]
|
| 947 |
+
os: [linux]
|
| 948 |
+
|
| 949 |
+
'@tailwindcss/oxide-linux-arm64-gnu@4.2.4':
|
| 950 |
+
resolution: {integrity: sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==}
|
| 951 |
+
engines: {node: '>= 20'}
|
| 952 |
+
cpu: [arm64]
|
| 953 |
+
os: [linux]
|
| 954 |
+
libc: [glibc]
|
| 955 |
+
|
| 956 |
+
'@tailwindcss/oxide-linux-arm64-musl@4.2.4':
|
| 957 |
+
resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==}
|
| 958 |
+
engines: {node: '>= 20'}
|
| 959 |
+
cpu: [arm64]
|
| 960 |
+
os: [linux]
|
| 961 |
+
libc: [musl]
|
| 962 |
+
|
| 963 |
+
'@tailwindcss/oxide-linux-x64-gnu@4.2.4':
|
| 964 |
+
resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==}
|
| 965 |
+
engines: {node: '>= 20'}
|
| 966 |
+
cpu: [x64]
|
| 967 |
+
os: [linux]
|
| 968 |
+
libc: [glibc]
|
| 969 |
+
|
| 970 |
+
'@tailwindcss/oxide-linux-x64-musl@4.2.4':
|
| 971 |
+
resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==}
|
| 972 |
+
engines: {node: '>= 20'}
|
| 973 |
+
cpu: [x64]
|
| 974 |
+
os: [linux]
|
| 975 |
+
libc: [musl]
|
| 976 |
+
|
| 977 |
+
'@tailwindcss/oxide-wasm32-wasi@4.2.4':
|
| 978 |
+
resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==}
|
| 979 |
+
engines: {node: '>=14.0.0'}
|
| 980 |
+
cpu: [wasm32]
|
| 981 |
+
bundledDependencies:
|
| 982 |
+
- '@napi-rs/wasm-runtime'
|
| 983 |
+
- '@emnapi/core'
|
| 984 |
+
- '@emnapi/runtime'
|
| 985 |
+
- '@tybys/wasm-util'
|
| 986 |
+
- '@emnapi/wasi-threads'
|
| 987 |
+
- tslib
|
| 988 |
+
|
| 989 |
+
'@tailwindcss/oxide-win32-arm64-msvc@4.2.4':
|
| 990 |
+
resolution: {integrity: sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==}
|
| 991 |
+
engines: {node: '>= 20'}
|
| 992 |
+
cpu: [arm64]
|
| 993 |
+
os: [win32]
|
| 994 |
+
|
| 995 |
+
'@tailwindcss/oxide-win32-x64-msvc@4.2.4':
|
| 996 |
+
resolution: {integrity: sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==}
|
| 997 |
+
engines: {node: '>= 20'}
|
| 998 |
+
cpu: [x64]
|
| 999 |
+
os: [win32]
|
| 1000 |
+
|
| 1001 |
+
'@tailwindcss/oxide@4.2.4':
|
| 1002 |
+
resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==}
|
| 1003 |
+
engines: {node: '>= 20'}
|
| 1004 |
+
|
| 1005 |
+
'@tailwindcss/vite@4.2.4':
|
| 1006 |
+
resolution: {integrity: sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==}
|
| 1007 |
+
peerDependencies:
|
| 1008 |
+
vite: ^5.2.0 || ^6 || ^7 || ^8
|
| 1009 |
+
|
| 1010 |
+
'@types/babel__core@7.20.5':
|
| 1011 |
+
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
| 1012 |
+
|
| 1013 |
+
'@types/babel__generator@7.27.0':
|
| 1014 |
+
resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
|
| 1015 |
+
|
| 1016 |
+
'@types/babel__template@7.4.4':
|
| 1017 |
+
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
|
| 1018 |
+
|
| 1019 |
+
'@types/babel__traverse@7.28.0':
|
| 1020 |
+
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
| 1021 |
+
|
| 1022 |
+
'@types/estree@1.0.8':
|
| 1023 |
+
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
| 1024 |
+
|
| 1025 |
+
'@types/react-dom@19.2.3':
|
| 1026 |
+
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
|
| 1027 |
+
peerDependencies:
|
| 1028 |
+
'@types/react': ^19.2.0
|
| 1029 |
+
|
| 1030 |
+
'@types/react@19.2.14':
|
| 1031 |
+
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
|
| 1032 |
+
|
| 1033 |
+
'@vitejs/plugin-react@4.7.0':
|
| 1034 |
+
resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
|
| 1035 |
+
engines: {node: ^14.18.0 || >=16.0.0}
|
| 1036 |
+
peerDependencies:
|
| 1037 |
+
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
| 1038 |
+
|
| 1039 |
+
aria-hidden@1.2.6:
|
| 1040 |
+
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
| 1041 |
+
engines: {node: '>=10'}
|
| 1042 |
+
|
| 1043 |
+
baseline-browser-mapping@2.10.23:
|
| 1044 |
+
resolution: {integrity: sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==}
|
| 1045 |
+
engines: {node: '>=6.0.0'}
|
| 1046 |
+
hasBin: true
|
| 1047 |
+
|
| 1048 |
+
browserslist@4.28.2:
|
| 1049 |
+
resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
|
| 1050 |
+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
| 1051 |
+
hasBin: true
|
| 1052 |
+
|
| 1053 |
+
caniuse-lite@1.0.30001791:
|
| 1054 |
+
resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==}
|
| 1055 |
+
|
| 1056 |
+
class-variance-authority@0.7.1:
|
| 1057 |
+
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
| 1058 |
+
|
| 1059 |
+
clsx@2.1.1:
|
| 1060 |
+
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
| 1061 |
+
engines: {node: '>=6'}
|
| 1062 |
+
|
| 1063 |
+
codemirror@6.0.2:
|
| 1064 |
+
resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
|
| 1065 |
+
|
| 1066 |
+
convert-source-map@2.0.0:
|
| 1067 |
+
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
| 1068 |
+
|
| 1069 |
+
crelt@1.0.6:
|
| 1070 |
+
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
| 1071 |
+
|
| 1072 |
+
csstype@3.2.3:
|
| 1073 |
+
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
| 1074 |
+
|
| 1075 |
+
debug@4.4.3:
|
| 1076 |
+
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
| 1077 |
+
engines: {node: '>=6.0'}
|
| 1078 |
+
peerDependencies:
|
| 1079 |
+
supports-color: '*'
|
| 1080 |
+
peerDependenciesMeta:
|
| 1081 |
+
supports-color:
|
| 1082 |
+
optional: true
|
| 1083 |
+
|
| 1084 |
+
detect-libc@2.1.2:
|
| 1085 |
+
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
| 1086 |
+
engines: {node: '>=8'}
|
| 1087 |
+
|
| 1088 |
+
detect-node-es@1.1.0:
|
| 1089 |
+
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
| 1090 |
+
|
| 1091 |
+
electron-to-chromium@1.5.344:
|
| 1092 |
+
resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==}
|
| 1093 |
+
|
| 1094 |
+
enhanced-resolve@5.21.0:
|
| 1095 |
+
resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==}
|
| 1096 |
+
engines: {node: '>=10.13.0'}
|
| 1097 |
+
|
| 1098 |
+
esbuild@0.25.12:
|
| 1099 |
+
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
| 1100 |
+
engines: {node: '>=18'}
|
| 1101 |
+
hasBin: true
|
| 1102 |
+
|
| 1103 |
+
escalade@3.2.0:
|
| 1104 |
+
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
| 1105 |
+
engines: {node: '>=6'}
|
| 1106 |
+
|
| 1107 |
+
fdir@6.5.0:
|
| 1108 |
+
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
| 1109 |
+
engines: {node: '>=12.0.0'}
|
| 1110 |
+
peerDependencies:
|
| 1111 |
+
picomatch: ^3 || ^4
|
| 1112 |
+
peerDependenciesMeta:
|
| 1113 |
+
picomatch:
|
| 1114 |
+
optional: true
|
| 1115 |
+
|
| 1116 |
+
framer-motion@12.38.0:
|
| 1117 |
+
resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==}
|
| 1118 |
+
peerDependencies:
|
| 1119 |
+
'@emotion/is-prop-valid': '*'
|
| 1120 |
+
react: ^18.0.0 || ^19.0.0
|
| 1121 |
+
react-dom: ^18.0.0 || ^19.0.0
|
| 1122 |
+
peerDependenciesMeta:
|
| 1123 |
+
'@emotion/is-prop-valid':
|
| 1124 |
+
optional: true
|
| 1125 |
+
react:
|
| 1126 |
+
optional: true
|
| 1127 |
+
react-dom:
|
| 1128 |
+
optional: true
|
| 1129 |
+
|
| 1130 |
+
fsevents@2.3.3:
|
| 1131 |
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
| 1132 |
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
| 1133 |
+
os: [darwin]
|
| 1134 |
+
|
| 1135 |
+
gensync@1.0.0-beta.2:
|
| 1136 |
+
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
| 1137 |
+
engines: {node: '>=6.9.0'}
|
| 1138 |
+
|
| 1139 |
+
get-nonce@1.0.1:
|
| 1140 |
+
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
| 1141 |
+
engines: {node: '>=6'}
|
| 1142 |
+
|
| 1143 |
+
graceful-fs@4.2.11:
|
| 1144 |
+
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
| 1145 |
+
|
| 1146 |
+
jiti@2.6.1:
|
| 1147 |
+
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
| 1148 |
+
hasBin: true
|
| 1149 |
+
|
| 1150 |
+
js-tokens@4.0.0:
|
| 1151 |
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
| 1152 |
+
|
| 1153 |
+
jsesc@3.1.0:
|
| 1154 |
+
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
| 1155 |
+
engines: {node: '>=6'}
|
| 1156 |
+
hasBin: true
|
| 1157 |
+
|
| 1158 |
+
json5@2.2.3:
|
| 1159 |
+
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
| 1160 |
+
engines: {node: '>=6'}
|
| 1161 |
+
hasBin: true
|
| 1162 |
+
|
| 1163 |
+
lightningcss-android-arm64@1.32.0:
|
| 1164 |
+
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
| 1165 |
+
engines: {node: '>= 12.0.0'}
|
| 1166 |
+
cpu: [arm64]
|
| 1167 |
+
os: [android]
|
| 1168 |
+
|
| 1169 |
+
lightningcss-darwin-arm64@1.32.0:
|
| 1170 |
+
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
|
| 1171 |
+
engines: {node: '>= 12.0.0'}
|
| 1172 |
+
cpu: [arm64]
|
| 1173 |
+
os: [darwin]
|
| 1174 |
+
|
| 1175 |
+
lightningcss-darwin-x64@1.32.0:
|
| 1176 |
+
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
|
| 1177 |
+
engines: {node: '>= 12.0.0'}
|
| 1178 |
+
cpu: [x64]
|
| 1179 |
+
os: [darwin]
|
| 1180 |
+
|
| 1181 |
+
lightningcss-freebsd-x64@1.32.0:
|
| 1182 |
+
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
|
| 1183 |
+
engines: {node: '>= 12.0.0'}
|
| 1184 |
+
cpu: [x64]
|
| 1185 |
+
os: [freebsd]
|
| 1186 |
+
|
| 1187 |
+
lightningcss-linux-arm-gnueabihf@1.32.0:
|
| 1188 |
+
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
|
| 1189 |
+
engines: {node: '>= 12.0.0'}
|
| 1190 |
+
cpu: [arm]
|
| 1191 |
+
os: [linux]
|
| 1192 |
+
|
| 1193 |
+
lightningcss-linux-arm64-gnu@1.32.0:
|
| 1194 |
+
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
|
| 1195 |
+
engines: {node: '>= 12.0.0'}
|
| 1196 |
+
cpu: [arm64]
|
| 1197 |
+
os: [linux]
|
| 1198 |
+
libc: [glibc]
|
| 1199 |
+
|
| 1200 |
+
lightningcss-linux-arm64-musl@1.32.0:
|
| 1201 |
+
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
| 1202 |
+
engines: {node: '>= 12.0.0'}
|
| 1203 |
+
cpu: [arm64]
|
| 1204 |
+
os: [linux]
|
| 1205 |
+
libc: [musl]
|
| 1206 |
+
|
| 1207 |
+
lightningcss-linux-x64-gnu@1.32.0:
|
| 1208 |
+
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
| 1209 |
+
engines: {node: '>= 12.0.0'}
|
| 1210 |
+
cpu: [x64]
|
| 1211 |
+
os: [linux]
|
| 1212 |
+
libc: [glibc]
|
| 1213 |
+
|
| 1214 |
+
lightningcss-linux-x64-musl@1.32.0:
|
| 1215 |
+
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
| 1216 |
+
engines: {node: '>= 12.0.0'}
|
| 1217 |
+
cpu: [x64]
|
| 1218 |
+
os: [linux]
|
| 1219 |
+
libc: [musl]
|
| 1220 |
+
|
| 1221 |
+
lightningcss-win32-arm64-msvc@1.32.0:
|
| 1222 |
+
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
| 1223 |
+
engines: {node: '>= 12.0.0'}
|
| 1224 |
+
cpu: [arm64]
|
| 1225 |
+
os: [win32]
|
| 1226 |
+
|
| 1227 |
+
lightningcss-win32-x64-msvc@1.32.0:
|
| 1228 |
+
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
|
| 1229 |
+
engines: {node: '>= 12.0.0'}
|
| 1230 |
+
cpu: [x64]
|
| 1231 |
+
os: [win32]
|
| 1232 |
+
|
| 1233 |
+
lightningcss@1.32.0:
|
| 1234 |
+
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
|
| 1235 |
+
engines: {node: '>= 12.0.0'}
|
| 1236 |
+
|
| 1237 |
+
lru-cache@5.1.1:
|
| 1238 |
+
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
| 1239 |
+
|
| 1240 |
+
lucide-react@0.453.0:
|
| 1241 |
+
resolution: {integrity: sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==}
|
| 1242 |
+
peerDependencies:
|
| 1243 |
+
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
|
| 1244 |
+
|
| 1245 |
+
magic-string@0.30.21:
|
| 1246 |
+
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
| 1247 |
+
|
| 1248 |
+
mitt@3.0.1:
|
| 1249 |
+
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
| 1250 |
+
|
| 1251 |
+
motion-dom@12.38.0:
|
| 1252 |
+
resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==}
|
| 1253 |
+
|
| 1254 |
+
motion-utils@12.36.0:
|
| 1255 |
+
resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==}
|
| 1256 |
+
|
| 1257 |
+
ms@2.1.3:
|
| 1258 |
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
| 1259 |
+
|
| 1260 |
+
nanoid@3.3.11:
|
| 1261 |
+
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
| 1262 |
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
| 1263 |
+
hasBin: true
|
| 1264 |
+
|
| 1265 |
+
node-releases@2.0.38:
|
| 1266 |
+
resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==}
|
| 1267 |
+
|
| 1268 |
+
picocolors@1.1.1:
|
| 1269 |
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
| 1270 |
+
|
| 1271 |
+
picomatch@4.0.4:
|
| 1272 |
+
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
| 1273 |
+
engines: {node: '>=12'}
|
| 1274 |
+
|
| 1275 |
+
postcss@8.5.12:
|
| 1276 |
+
resolution: {integrity: sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==}
|
| 1277 |
+
engines: {node: ^10 || ^12 || >=14}
|
| 1278 |
+
|
| 1279 |
+
react-dom@19.2.5:
|
| 1280 |
+
resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==}
|
| 1281 |
+
peerDependencies:
|
| 1282 |
+
react: ^19.2.5
|
| 1283 |
+
|
| 1284 |
+
react-refresh@0.17.0:
|
| 1285 |
+
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
| 1286 |
+
engines: {node: '>=0.10.0'}
|
| 1287 |
+
|
| 1288 |
+
react-remove-scroll-bar@2.3.8:
|
| 1289 |
+
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
| 1290 |
+
engines: {node: '>=10'}
|
| 1291 |
+
peerDependencies:
|
| 1292 |
+
'@types/react': '*'
|
| 1293 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 1294 |
+
peerDependenciesMeta:
|
| 1295 |
+
'@types/react':
|
| 1296 |
+
optional: true
|
| 1297 |
+
|
| 1298 |
+
react-remove-scroll@2.7.2:
|
| 1299 |
+
resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==}
|
| 1300 |
+
engines: {node: '>=10'}
|
| 1301 |
+
peerDependencies:
|
| 1302 |
+
'@types/react': '*'
|
| 1303 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1304 |
+
peerDependenciesMeta:
|
| 1305 |
+
'@types/react':
|
| 1306 |
+
optional: true
|
| 1307 |
+
|
| 1308 |
+
react-resizable-panels@3.0.6:
|
| 1309 |
+
resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==}
|
| 1310 |
+
peerDependencies:
|
| 1311 |
+
react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1312 |
+
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1313 |
+
|
| 1314 |
+
react-style-singleton@2.2.3:
|
| 1315 |
+
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
| 1316 |
+
engines: {node: '>=10'}
|
| 1317 |
+
peerDependencies:
|
| 1318 |
+
'@types/react': '*'
|
| 1319 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1320 |
+
peerDependenciesMeta:
|
| 1321 |
+
'@types/react':
|
| 1322 |
+
optional: true
|
| 1323 |
+
|
| 1324 |
+
react@19.2.5:
|
| 1325 |
+
resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==}
|
| 1326 |
+
engines: {node: '>=0.10.0'}
|
| 1327 |
+
|
| 1328 |
+
regexparam@3.0.0:
|
| 1329 |
+
resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==}
|
| 1330 |
+
engines: {node: '>=8'}
|
| 1331 |
+
|
| 1332 |
+
rollup@4.60.2:
|
| 1333 |
+
resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==}
|
| 1334 |
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
| 1335 |
+
hasBin: true
|
| 1336 |
+
|
| 1337 |
+
scheduler@0.27.0:
|
| 1338 |
+
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
|
| 1339 |
+
|
| 1340 |
+
semver@6.3.1:
|
| 1341 |
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
| 1342 |
+
hasBin: true
|
| 1343 |
+
|
| 1344 |
+
sonner@2.0.7:
|
| 1345 |
+
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
|
| 1346 |
+
peerDependencies:
|
| 1347 |
+
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1348 |
+
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1349 |
+
|
| 1350 |
+
source-map-js@1.2.1:
|
| 1351 |
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
| 1352 |
+
engines: {node: '>=0.10.0'}
|
| 1353 |
+
|
| 1354 |
+
style-mod@4.1.3:
|
| 1355 |
+
resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==}
|
| 1356 |
+
|
| 1357 |
+
swagger-ui-dist@5.32.5:
|
| 1358 |
+
resolution: {integrity: sha512-7/FQfWe9A4qoyYFdAwy0chD0uDYidDp/ZT9VQ9LZlgD4AnnHJk8/+ytAA1HkJYOPySmK6helPDdJQMlcumt7HA==}
|
| 1359 |
+
|
| 1360 |
+
tailwind-merge@3.5.0:
|
| 1361 |
+
resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==}
|
| 1362 |
+
|
| 1363 |
+
tailwindcss@4.2.4:
|
| 1364 |
+
resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==}
|
| 1365 |
+
|
| 1366 |
+
tapable@2.3.3:
|
| 1367 |
+
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
| 1368 |
+
engines: {node: '>=6'}
|
| 1369 |
+
|
| 1370 |
+
tinyglobby@0.2.16:
|
| 1371 |
+
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
|
| 1372 |
+
engines: {node: '>=12.0.0'}
|
| 1373 |
+
|
| 1374 |
+
tslib@2.8.1:
|
| 1375 |
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
| 1376 |
+
|
| 1377 |
+
typescript@5.9.3:
|
| 1378 |
+
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
| 1379 |
+
engines: {node: '>=14.17'}
|
| 1380 |
+
hasBin: true
|
| 1381 |
+
|
| 1382 |
+
update-browserslist-db@1.2.3:
|
| 1383 |
+
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
|
| 1384 |
+
hasBin: true
|
| 1385 |
+
peerDependencies:
|
| 1386 |
+
browserslist: '>= 4.21.0'
|
| 1387 |
+
|
| 1388 |
+
use-callback-ref@1.3.3:
|
| 1389 |
+
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
|
| 1390 |
+
engines: {node: '>=10'}
|
| 1391 |
+
peerDependencies:
|
| 1392 |
+
'@types/react': '*'
|
| 1393 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1394 |
+
peerDependenciesMeta:
|
| 1395 |
+
'@types/react':
|
| 1396 |
+
optional: true
|
| 1397 |
+
|
| 1398 |
+
use-sidecar@1.1.3:
|
| 1399 |
+
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
| 1400 |
+
engines: {node: '>=10'}
|
| 1401 |
+
peerDependencies:
|
| 1402 |
+
'@types/react': '*'
|
| 1403 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
| 1404 |
+
peerDependenciesMeta:
|
| 1405 |
+
'@types/react':
|
| 1406 |
+
optional: true
|
| 1407 |
+
|
| 1408 |
+
use-sync-external-store@1.6.0:
|
| 1409 |
+
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
| 1410 |
+
peerDependencies:
|
| 1411 |
+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
| 1412 |
+
|
| 1413 |
+
vite@6.4.2:
|
| 1414 |
+
resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==}
|
| 1415 |
+
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
| 1416 |
+
hasBin: true
|
| 1417 |
+
peerDependencies:
|
| 1418 |
+
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
| 1419 |
+
jiti: '>=1.21.0'
|
| 1420 |
+
less: '*'
|
| 1421 |
+
lightningcss: ^1.21.0
|
| 1422 |
+
sass: '*'
|
| 1423 |
+
sass-embedded: '*'
|
| 1424 |
+
stylus: '*'
|
| 1425 |
+
sugarss: '*'
|
| 1426 |
+
terser: ^5.16.0
|
| 1427 |
+
tsx: ^4.8.1
|
| 1428 |
+
yaml: ^2.4.2
|
| 1429 |
+
peerDependenciesMeta:
|
| 1430 |
+
'@types/node':
|
| 1431 |
+
optional: true
|
| 1432 |
+
jiti:
|
| 1433 |
+
optional: true
|
| 1434 |
+
less:
|
| 1435 |
+
optional: true
|
| 1436 |
+
lightningcss:
|
| 1437 |
+
optional: true
|
| 1438 |
+
sass:
|
| 1439 |
+
optional: true
|
| 1440 |
+
sass-embedded:
|
| 1441 |
+
optional: true
|
| 1442 |
+
stylus:
|
| 1443 |
+
optional: true
|
| 1444 |
+
sugarss:
|
| 1445 |
+
optional: true
|
| 1446 |
+
terser:
|
| 1447 |
+
optional: true
|
| 1448 |
+
tsx:
|
| 1449 |
+
optional: true
|
| 1450 |
+
yaml:
|
| 1451 |
+
optional: true
|
| 1452 |
+
|
| 1453 |
+
w3c-keyname@2.2.8:
|
| 1454 |
+
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
| 1455 |
+
|
| 1456 |
+
wouter@3.9.0:
|
| 1457 |
+
resolution: {integrity: sha512-sF/od/PIgqEQBQcrN7a2x3MX6MQE6nW0ygCfy9hQuUkuB28wEZuu/6M5GyqkrrEu9M6jxdkgE12yDFsQMKos4Q==}
|
| 1458 |
+
peerDependencies:
|
| 1459 |
+
react: '>=16.8.0'
|
| 1460 |
+
|
| 1461 |
+
yallist@3.1.1:
|
| 1462 |
+
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
| 1463 |
+
|
| 1464 |
+
snapshots:
|
| 1465 |
+
|
| 1466 |
+
'@babel/code-frame@7.29.0':
|
| 1467 |
+
dependencies:
|
| 1468 |
+
'@babel/helper-validator-identifier': 7.28.5
|
| 1469 |
+
js-tokens: 4.0.0
|
| 1470 |
+
picocolors: 1.1.1
|
| 1471 |
+
|
| 1472 |
+
'@babel/compat-data@7.29.0': {}
|
| 1473 |
+
|
| 1474 |
+
'@babel/core@7.29.0':
|
| 1475 |
+
dependencies:
|
| 1476 |
+
'@babel/code-frame': 7.29.0
|
| 1477 |
+
'@babel/generator': 7.29.1
|
| 1478 |
+
'@babel/helper-compilation-targets': 7.28.6
|
| 1479 |
+
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
|
| 1480 |
+
'@babel/helpers': 7.29.2
|
| 1481 |
+
'@babel/parser': 7.29.2
|
| 1482 |
+
'@babel/template': 7.28.6
|
| 1483 |
+
'@babel/traverse': 7.29.0
|
| 1484 |
+
'@babel/types': 7.29.0
|
| 1485 |
+
'@jridgewell/remapping': 2.3.5
|
| 1486 |
+
convert-source-map: 2.0.0
|
| 1487 |
+
debug: 4.4.3
|
| 1488 |
+
gensync: 1.0.0-beta.2
|
| 1489 |
+
json5: 2.2.3
|
| 1490 |
+
semver: 6.3.1
|
| 1491 |
+
transitivePeerDependencies:
|
| 1492 |
+
- supports-color
|
| 1493 |
+
|
| 1494 |
+
'@babel/generator@7.29.1':
|
| 1495 |
+
dependencies:
|
| 1496 |
+
'@babel/parser': 7.29.2
|
| 1497 |
+
'@babel/types': 7.29.0
|
| 1498 |
+
'@jridgewell/gen-mapping': 0.3.13
|
| 1499 |
+
'@jridgewell/trace-mapping': 0.3.31
|
| 1500 |
+
jsesc: 3.1.0
|
| 1501 |
+
|
| 1502 |
+
'@babel/helper-compilation-targets@7.28.6':
|
| 1503 |
+
dependencies:
|
| 1504 |
+
'@babel/compat-data': 7.29.0
|
| 1505 |
+
'@babel/helper-validator-option': 7.27.1
|
| 1506 |
+
browserslist: 4.28.2
|
| 1507 |
+
lru-cache: 5.1.1
|
| 1508 |
+
semver: 6.3.1
|
| 1509 |
+
|
| 1510 |
+
'@babel/helper-globals@7.28.0': {}
|
| 1511 |
+
|
| 1512 |
+
'@babel/helper-module-imports@7.28.6':
|
| 1513 |
+
dependencies:
|
| 1514 |
+
'@babel/traverse': 7.29.0
|
| 1515 |
+
'@babel/types': 7.29.0
|
| 1516 |
+
transitivePeerDependencies:
|
| 1517 |
+
- supports-color
|
| 1518 |
+
|
| 1519 |
+
'@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
|
| 1520 |
+
dependencies:
|
| 1521 |
+
'@babel/core': 7.29.0
|
| 1522 |
+
'@babel/helper-module-imports': 7.28.6
|
| 1523 |
+
'@babel/helper-validator-identifier': 7.28.5
|
| 1524 |
+
'@babel/traverse': 7.29.0
|
| 1525 |
+
transitivePeerDependencies:
|
| 1526 |
+
- supports-color
|
| 1527 |
+
|
| 1528 |
+
'@babel/helper-plugin-utils@7.28.6': {}
|
| 1529 |
+
|
| 1530 |
+
'@babel/helper-string-parser@7.27.1': {}
|
| 1531 |
+
|
| 1532 |
+
'@babel/helper-validator-identifier@7.28.5': {}
|
| 1533 |
+
|
| 1534 |
+
'@babel/helper-validator-option@7.27.1': {}
|
| 1535 |
+
|
| 1536 |
+
'@babel/helpers@7.29.2':
|
| 1537 |
+
dependencies:
|
| 1538 |
+
'@babel/template': 7.28.6
|
| 1539 |
+
'@babel/types': 7.29.0
|
| 1540 |
+
|
| 1541 |
+
'@babel/parser@7.29.2':
|
| 1542 |
+
dependencies:
|
| 1543 |
+
'@babel/types': 7.29.0
|
| 1544 |
+
|
| 1545 |
+
'@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)':
|
| 1546 |
+
dependencies:
|
| 1547 |
+
'@babel/core': 7.29.0
|
| 1548 |
+
'@babel/helper-plugin-utils': 7.28.6
|
| 1549 |
+
|
| 1550 |
+
'@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)':
|
| 1551 |
+
dependencies:
|
| 1552 |
+
'@babel/core': 7.29.0
|
| 1553 |
+
'@babel/helper-plugin-utils': 7.28.6
|
| 1554 |
+
|
| 1555 |
+
'@babel/template@7.28.6':
|
| 1556 |
+
dependencies:
|
| 1557 |
+
'@babel/code-frame': 7.29.0
|
| 1558 |
+
'@babel/parser': 7.29.2
|
| 1559 |
+
'@babel/types': 7.29.0
|
| 1560 |
+
|
| 1561 |
+
'@babel/traverse@7.29.0':
|
| 1562 |
+
dependencies:
|
| 1563 |
+
'@babel/code-frame': 7.29.0
|
| 1564 |
+
'@babel/generator': 7.29.1
|
| 1565 |
+
'@babel/helper-globals': 7.28.0
|
| 1566 |
+
'@babel/parser': 7.29.2
|
| 1567 |
+
'@babel/template': 7.28.6
|
| 1568 |
+
'@babel/types': 7.29.0
|
| 1569 |
+
debug: 4.4.3
|
| 1570 |
+
transitivePeerDependencies:
|
| 1571 |
+
- supports-color
|
| 1572 |
+
|
| 1573 |
+
'@babel/types@7.29.0':
|
| 1574 |
+
dependencies:
|
| 1575 |
+
'@babel/helper-string-parser': 7.27.1
|
| 1576 |
+
'@babel/helper-validator-identifier': 7.28.5
|
| 1577 |
+
|
| 1578 |
+
'@codemirror/autocomplete@6.20.1':
|
| 1579 |
+
dependencies:
|
| 1580 |
+
'@codemirror/language': 6.12.3
|
| 1581 |
+
'@codemirror/state': 6.6.0
|
| 1582 |
+
'@codemirror/view': 6.41.1
|
| 1583 |
+
'@lezer/common': 1.5.2
|
| 1584 |
+
|
| 1585 |
+
'@codemirror/commands@6.10.3':
|
| 1586 |
+
dependencies:
|
| 1587 |
+
'@codemirror/language': 6.12.3
|
| 1588 |
+
'@codemirror/state': 6.6.0
|
| 1589 |
+
'@codemirror/view': 6.41.1
|
| 1590 |
+
'@lezer/common': 1.5.2
|
| 1591 |
+
|
| 1592 |
+
'@codemirror/lang-sql@6.10.0':
|
| 1593 |
+
dependencies:
|
| 1594 |
+
'@codemirror/autocomplete': 6.20.1
|
| 1595 |
+
'@codemirror/language': 6.12.3
|
| 1596 |
+
'@codemirror/state': 6.6.0
|
| 1597 |
+
'@lezer/common': 1.5.2
|
| 1598 |
+
'@lezer/highlight': 1.2.3
|
| 1599 |
+
'@lezer/lr': 1.4.10
|
| 1600 |
+
|
| 1601 |
+
'@codemirror/language@6.12.3':
|
| 1602 |
+
dependencies:
|
| 1603 |
+
'@codemirror/state': 6.6.0
|
| 1604 |
+
'@codemirror/view': 6.41.1
|
| 1605 |
+
'@lezer/common': 1.5.2
|
| 1606 |
+
'@lezer/highlight': 1.2.3
|
| 1607 |
+
'@lezer/lr': 1.4.10
|
| 1608 |
+
style-mod: 4.1.3
|
| 1609 |
+
|
| 1610 |
+
'@codemirror/lint@6.9.5':
|
| 1611 |
+
dependencies:
|
| 1612 |
+
'@codemirror/state': 6.6.0
|
| 1613 |
+
'@codemirror/view': 6.41.1
|
| 1614 |
+
crelt: 1.0.6
|
| 1615 |
+
|
| 1616 |
+
'@codemirror/search@6.7.0':
|
| 1617 |
+
dependencies:
|
| 1618 |
+
'@codemirror/state': 6.6.0
|
| 1619 |
+
'@codemirror/view': 6.41.1
|
| 1620 |
+
crelt: 1.0.6
|
| 1621 |
+
|
| 1622 |
+
'@codemirror/state@6.6.0':
|
| 1623 |
+
dependencies:
|
| 1624 |
+
'@marijn/find-cluster-break': 1.0.2
|
| 1625 |
+
|
| 1626 |
+
'@codemirror/theme-one-dark@6.1.3':
|
| 1627 |
+
dependencies:
|
| 1628 |
+
'@codemirror/language': 6.12.3
|
| 1629 |
+
'@codemirror/state': 6.6.0
|
| 1630 |
+
'@codemirror/view': 6.41.1
|
| 1631 |
+
'@lezer/highlight': 1.2.3
|
| 1632 |
+
|
| 1633 |
+
'@codemirror/view@6.41.1':
|
| 1634 |
+
dependencies:
|
| 1635 |
+
'@codemirror/state': 6.6.0
|
| 1636 |
+
crelt: 1.0.6
|
| 1637 |
+
style-mod: 4.1.3
|
| 1638 |
+
w3c-keyname: 2.2.8
|
| 1639 |
+
|
| 1640 |
+
'@esbuild/aix-ppc64@0.25.12':
|
| 1641 |
+
optional: true
|
| 1642 |
+
|
| 1643 |
+
'@esbuild/android-arm64@0.25.12':
|
| 1644 |
+
optional: true
|
| 1645 |
+
|
| 1646 |
+
'@esbuild/android-arm@0.25.12':
|
| 1647 |
+
optional: true
|
| 1648 |
+
|
| 1649 |
+
'@esbuild/android-x64@0.25.12':
|
| 1650 |
+
optional: true
|
| 1651 |
+
|
| 1652 |
+
'@esbuild/darwin-arm64@0.25.12':
|
| 1653 |
+
optional: true
|
| 1654 |
+
|
| 1655 |
+
'@esbuild/darwin-x64@0.25.12':
|
| 1656 |
+
optional: true
|
| 1657 |
+
|
| 1658 |
+
'@esbuild/freebsd-arm64@0.25.12':
|
| 1659 |
+
optional: true
|
| 1660 |
+
|
| 1661 |
+
'@esbuild/freebsd-x64@0.25.12':
|
| 1662 |
+
optional: true
|
| 1663 |
+
|
| 1664 |
+
'@esbuild/linux-arm64@0.25.12':
|
| 1665 |
+
optional: true
|
| 1666 |
+
|
| 1667 |
+
'@esbuild/linux-arm@0.25.12':
|
| 1668 |
+
optional: true
|
| 1669 |
+
|
| 1670 |
+
'@esbuild/linux-ia32@0.25.12':
|
| 1671 |
+
optional: true
|
| 1672 |
+
|
| 1673 |
+
'@esbuild/linux-loong64@0.25.12':
|
| 1674 |
+
optional: true
|
| 1675 |
+
|
| 1676 |
+
'@esbuild/linux-mips64el@0.25.12':
|
| 1677 |
+
optional: true
|
| 1678 |
+
|
| 1679 |
+
'@esbuild/linux-ppc64@0.25.12':
|
| 1680 |
+
optional: true
|
| 1681 |
+
|
| 1682 |
+
'@esbuild/linux-riscv64@0.25.12':
|
| 1683 |
+
optional: true
|
| 1684 |
+
|
| 1685 |
+
'@esbuild/linux-s390x@0.25.12':
|
| 1686 |
+
optional: true
|
| 1687 |
+
|
| 1688 |
+
'@esbuild/linux-x64@0.25.12':
|
| 1689 |
+
optional: true
|
| 1690 |
+
|
| 1691 |
+
'@esbuild/netbsd-arm64@0.25.12':
|
| 1692 |
+
optional: true
|
| 1693 |
+
|
| 1694 |
+
'@esbuild/netbsd-x64@0.25.12':
|
| 1695 |
+
optional: true
|
| 1696 |
+
|
| 1697 |
+
'@esbuild/openbsd-arm64@0.25.12':
|
| 1698 |
+
optional: true
|
| 1699 |
+
|
| 1700 |
+
'@esbuild/openbsd-x64@0.25.12':
|
| 1701 |
+
optional: true
|
| 1702 |
+
|
| 1703 |
+
'@esbuild/openharmony-arm64@0.25.12':
|
| 1704 |
+
optional: true
|
| 1705 |
+
|
| 1706 |
+
'@esbuild/sunos-x64@0.25.12':
|
| 1707 |
+
optional: true
|
| 1708 |
+
|
| 1709 |
+
'@esbuild/win32-arm64@0.25.12':
|
| 1710 |
+
optional: true
|
| 1711 |
+
|
| 1712 |
+
'@esbuild/win32-ia32@0.25.12':
|
| 1713 |
+
optional: true
|
| 1714 |
+
|
| 1715 |
+
'@esbuild/win32-x64@0.25.12':
|
| 1716 |
+
optional: true
|
| 1717 |
+
|
| 1718 |
+
'@floating-ui/core@1.7.5':
|
| 1719 |
+
dependencies:
|
| 1720 |
+
'@floating-ui/utils': 0.2.11
|
| 1721 |
+
|
| 1722 |
+
'@floating-ui/dom@1.7.6':
|
| 1723 |
+
dependencies:
|
| 1724 |
+
'@floating-ui/core': 1.7.5
|
| 1725 |
+
'@floating-ui/utils': 0.2.11
|
| 1726 |
+
|
| 1727 |
+
'@floating-ui/react-dom@2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1728 |
+
dependencies:
|
| 1729 |
+
'@floating-ui/dom': 1.7.6
|
| 1730 |
+
react: 19.2.5
|
| 1731 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1732 |
+
|
| 1733 |
+
'@floating-ui/utils@0.2.11': {}
|
| 1734 |
+
|
| 1735 |
+
'@jridgewell/gen-mapping@0.3.13':
|
| 1736 |
+
dependencies:
|
| 1737 |
+
'@jridgewell/sourcemap-codec': 1.5.5
|
| 1738 |
+
'@jridgewell/trace-mapping': 0.3.31
|
| 1739 |
+
|
| 1740 |
+
'@jridgewell/remapping@2.3.5':
|
| 1741 |
+
dependencies:
|
| 1742 |
+
'@jridgewell/gen-mapping': 0.3.13
|
| 1743 |
+
'@jridgewell/trace-mapping': 0.3.31
|
| 1744 |
+
|
| 1745 |
+
'@jridgewell/resolve-uri@3.1.2': {}
|
| 1746 |
+
|
| 1747 |
+
'@jridgewell/sourcemap-codec@1.5.5': {}
|
| 1748 |
+
|
| 1749 |
+
'@jridgewell/trace-mapping@0.3.31':
|
| 1750 |
+
dependencies:
|
| 1751 |
+
'@jridgewell/resolve-uri': 3.1.2
|
| 1752 |
+
'@jridgewell/sourcemap-codec': 1.5.5
|
| 1753 |
+
|
| 1754 |
+
'@lezer/common@1.5.2': {}
|
| 1755 |
+
|
| 1756 |
+
'@lezer/highlight@1.2.3':
|
| 1757 |
+
dependencies:
|
| 1758 |
+
'@lezer/common': 1.5.2
|
| 1759 |
+
|
| 1760 |
+
'@lezer/lr@1.4.10':
|
| 1761 |
+
dependencies:
|
| 1762 |
+
'@lezer/common': 1.5.2
|
| 1763 |
+
|
| 1764 |
+
'@marijn/find-cluster-break@1.0.2': {}
|
| 1765 |
+
|
| 1766 |
+
'@radix-ui/number@1.1.1': {}
|
| 1767 |
+
|
| 1768 |
+
'@radix-ui/primitive@1.1.3': {}
|
| 1769 |
+
|
| 1770 |
+
'@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1771 |
+
dependencies:
|
| 1772 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1773 |
+
react: 19.2.5
|
| 1774 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1775 |
+
optionalDependencies:
|
| 1776 |
+
'@types/react': 19.2.14
|
| 1777 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1778 |
+
|
| 1779 |
+
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1780 |
+
dependencies:
|
| 1781 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1782 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1783 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1784 |
+
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 1785 |
+
react: 19.2.5
|
| 1786 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1787 |
+
optionalDependencies:
|
| 1788 |
+
'@types/react': 19.2.14
|
| 1789 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1790 |
+
|
| 1791 |
+
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.5)':
|
| 1792 |
+
dependencies:
|
| 1793 |
+
react: 19.2.5
|
| 1794 |
+
optionalDependencies:
|
| 1795 |
+
'@types/react': 19.2.14
|
| 1796 |
+
|
| 1797 |
+
'@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.5)':
|
| 1798 |
+
dependencies:
|
| 1799 |
+
react: 19.2.5
|
| 1800 |
+
optionalDependencies:
|
| 1801 |
+
'@types/react': 19.2.14
|
| 1802 |
+
|
| 1803 |
+
'@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 1804 |
+
dependencies:
|
| 1805 |
+
react: 19.2.5
|
| 1806 |
+
optionalDependencies:
|
| 1807 |
+
'@types/react': 19.2.14
|
| 1808 |
+
|
| 1809 |
+
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1810 |
+
dependencies:
|
| 1811 |
+
'@radix-ui/primitive': 1.1.3
|
| 1812 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1813 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1814 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1815 |
+
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1816 |
+
react: 19.2.5
|
| 1817 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1818 |
+
optionalDependencies:
|
| 1819 |
+
'@types/react': 19.2.14
|
| 1820 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1821 |
+
|
| 1822 |
+
'@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.5)':
|
| 1823 |
+
dependencies:
|
| 1824 |
+
react: 19.2.5
|
| 1825 |
+
optionalDependencies:
|
| 1826 |
+
'@types/react': 19.2.14
|
| 1827 |
+
|
| 1828 |
+
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1829 |
+
dependencies:
|
| 1830 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1831 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1832 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1833 |
+
react: 19.2.5
|
| 1834 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1835 |
+
optionalDependencies:
|
| 1836 |
+
'@types/react': 19.2.14
|
| 1837 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1838 |
+
|
| 1839 |
+
'@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 1840 |
+
dependencies:
|
| 1841 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1842 |
+
react: 19.2.5
|
| 1843 |
+
optionalDependencies:
|
| 1844 |
+
'@types/react': 19.2.14
|
| 1845 |
+
|
| 1846 |
+
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1847 |
+
dependencies:
|
| 1848 |
+
'@floating-ui/react-dom': 2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1849 |
+
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1850 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1851 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1852 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1853 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1854 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1855 |
+
'@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1856 |
+
'@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1857 |
+
'@radix-ui/rect': 1.1.1
|
| 1858 |
+
react: 19.2.5
|
| 1859 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1860 |
+
optionalDependencies:
|
| 1861 |
+
'@types/react': 19.2.14
|
| 1862 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1863 |
+
|
| 1864 |
+
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1865 |
+
dependencies:
|
| 1866 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1867 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1868 |
+
react: 19.2.5
|
| 1869 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1870 |
+
optionalDependencies:
|
| 1871 |
+
'@types/react': 19.2.14
|
| 1872 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1873 |
+
|
| 1874 |
+
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1875 |
+
dependencies:
|
| 1876 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1877 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1878 |
+
react: 19.2.5
|
| 1879 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1880 |
+
optionalDependencies:
|
| 1881 |
+
'@types/react': 19.2.14
|
| 1882 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1883 |
+
|
| 1884 |
+
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1885 |
+
dependencies:
|
| 1886 |
+
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 1887 |
+
react: 19.2.5
|
| 1888 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1889 |
+
optionalDependencies:
|
| 1890 |
+
'@types/react': 19.2.14
|
| 1891 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1892 |
+
|
| 1893 |
+
'@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1894 |
+
dependencies:
|
| 1895 |
+
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5)
|
| 1896 |
+
react: 19.2.5
|
| 1897 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1898 |
+
optionalDependencies:
|
| 1899 |
+
'@types/react': 19.2.14
|
| 1900 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1901 |
+
|
| 1902 |
+
'@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1903 |
+
dependencies:
|
| 1904 |
+
'@radix-ui/primitive': 1.1.3
|
| 1905 |
+
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1906 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1907 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1908 |
+
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1909 |
+
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1910 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1911 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1912 |
+
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5)
|
| 1913 |
+
react: 19.2.5
|
| 1914 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1915 |
+
optionalDependencies:
|
| 1916 |
+
'@types/react': 19.2.14
|
| 1917 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1918 |
+
|
| 1919 |
+
'@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1920 |
+
dependencies:
|
| 1921 |
+
'@radix-ui/number': 1.1.1
|
| 1922 |
+
'@radix-ui/primitive': 1.1.3
|
| 1923 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1924 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1925 |
+
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1926 |
+
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1927 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1928 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1929 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1930 |
+
react: 19.2.5
|
| 1931 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1932 |
+
optionalDependencies:
|
| 1933 |
+
'@types/react': 19.2.14
|
| 1934 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1935 |
+
|
| 1936 |
+
'@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1937 |
+
dependencies:
|
| 1938 |
+
'@radix-ui/number': 1.1.1
|
| 1939 |
+
'@radix-ui/primitive': 1.1.3
|
| 1940 |
+
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1941 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1942 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1943 |
+
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1944 |
+
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1945 |
+
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5)
|
| 1946 |
+
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1947 |
+
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1948 |
+
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1949 |
+
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1950 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1951 |
+
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 1952 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1953 |
+
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5)
|
| 1954 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1955 |
+
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1956 |
+
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1957 |
+
aria-hidden: 1.2.6
|
| 1958 |
+
react: 19.2.5
|
| 1959 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1960 |
+
react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5)
|
| 1961 |
+
optionalDependencies:
|
| 1962 |
+
'@types/react': 19.2.14
|
| 1963 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1964 |
+
|
| 1965 |
+
'@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1966 |
+
dependencies:
|
| 1967 |
+
'@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1968 |
+
react: 19.2.5
|
| 1969 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 1970 |
+
optionalDependencies:
|
| 1971 |
+
'@types/react': 19.2.14
|
| 1972 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 1973 |
+
|
| 1974 |
+
'@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.5)':
|
| 1975 |
+
dependencies:
|
| 1976 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1977 |
+
react: 19.2.5
|
| 1978 |
+
optionalDependencies:
|
| 1979 |
+
'@types/react': 19.2.14
|
| 1980 |
+
|
| 1981 |
+
'@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.5)':
|
| 1982 |
+
dependencies:
|
| 1983 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1984 |
+
react: 19.2.5
|
| 1985 |
+
optionalDependencies:
|
| 1986 |
+
'@types/react': 19.2.14
|
| 1987 |
+
|
| 1988 |
+
'@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 1989 |
+
dependencies:
|
| 1990 |
+
'@radix-ui/primitive': 1.1.3
|
| 1991 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 1992 |
+
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1993 |
+
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 1994 |
+
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1995 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1996 |
+
'@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 1997 |
+
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5)
|
| 1998 |
+
react: 19.2.5
|
| 1999 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2000 |
+
optionalDependencies:
|
| 2001 |
+
'@types/react': 19.2.14
|
| 2002 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 2003 |
+
|
| 2004 |
+
'@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 2005 |
+
dependencies:
|
| 2006 |
+
'@radix-ui/primitive': 1.1.3
|
| 2007 |
+
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 2008 |
+
'@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
| 2009 |
+
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2010 |
+
'@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 2011 |
+
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2012 |
+
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2013 |
+
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2014 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2015 |
+
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 2016 |
+
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5)
|
| 2017 |
+
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2018 |
+
react: 19.2.5
|
| 2019 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2020 |
+
optionalDependencies:
|
| 2021 |
+
'@types/react': 19.2.14
|
| 2022 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 2023 |
+
|
| 2024 |
+
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2025 |
+
dependencies:
|
| 2026 |
+
react: 19.2.5
|
| 2027 |
+
optionalDependencies:
|
| 2028 |
+
'@types/react': 19.2.14
|
| 2029 |
+
|
| 2030 |
+
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.5)':
|
| 2031 |
+
dependencies:
|
| 2032 |
+
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.5)
|
| 2033 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 2034 |
+
react: 19.2.5
|
| 2035 |
+
optionalDependencies:
|
| 2036 |
+
'@types/react': 19.2.14
|
| 2037 |
+
|
| 2038 |
+
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.5)':
|
| 2039 |
+
dependencies:
|
| 2040 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 2041 |
+
react: 19.2.5
|
| 2042 |
+
optionalDependencies:
|
| 2043 |
+
'@types/react': 19.2.14
|
| 2044 |
+
|
| 2045 |
+
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2046 |
+
dependencies:
|
| 2047 |
+
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 2048 |
+
react: 19.2.5
|
| 2049 |
+
optionalDependencies:
|
| 2050 |
+
'@types/react': 19.2.14
|
| 2051 |
+
|
| 2052 |
+
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2053 |
+
dependencies:
|
| 2054 |
+
react: 19.2.5
|
| 2055 |
+
optionalDependencies:
|
| 2056 |
+
'@types/react': 19.2.14
|
| 2057 |
+
|
| 2058 |
+
'@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2059 |
+
dependencies:
|
| 2060 |
+
react: 19.2.5
|
| 2061 |
+
optionalDependencies:
|
| 2062 |
+
'@types/react': 19.2.14
|
| 2063 |
+
|
| 2064 |
+
'@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2065 |
+
dependencies:
|
| 2066 |
+
'@radix-ui/rect': 1.1.1
|
| 2067 |
+
react: 19.2.5
|
| 2068 |
+
optionalDependencies:
|
| 2069 |
+
'@types/react': 19.2.14
|
| 2070 |
+
|
| 2071 |
+
'@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.5)':
|
| 2072 |
+
dependencies:
|
| 2073 |
+
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5)
|
| 2074 |
+
react: 19.2.5
|
| 2075 |
+
optionalDependencies:
|
| 2076 |
+
'@types/react': 19.2.14
|
| 2077 |
+
|
| 2078 |
+
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
| 2079 |
+
dependencies:
|
| 2080 |
+
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
| 2081 |
+
react: 19.2.5
|
| 2082 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2083 |
+
optionalDependencies:
|
| 2084 |
+
'@types/react': 19.2.14
|
| 2085 |
+
'@types/react-dom': 19.2.3(@types/react@19.2.14)
|
| 2086 |
+
|
| 2087 |
+
'@radix-ui/rect@1.1.1': {}
|
| 2088 |
+
|
| 2089 |
+
'@rolldown/pluginutils@1.0.0-beta.27': {}
|
| 2090 |
+
|
| 2091 |
+
'@rollup/rollup-android-arm-eabi@4.60.2':
|
| 2092 |
+
optional: true
|
| 2093 |
+
|
| 2094 |
+
'@rollup/rollup-android-arm64@4.60.2':
|
| 2095 |
+
optional: true
|
| 2096 |
+
|
| 2097 |
+
'@rollup/rollup-darwin-arm64@4.60.2':
|
| 2098 |
+
optional: true
|
| 2099 |
+
|
| 2100 |
+
'@rollup/rollup-darwin-x64@4.60.2':
|
| 2101 |
+
optional: true
|
| 2102 |
+
|
| 2103 |
+
'@rollup/rollup-freebsd-arm64@4.60.2':
|
| 2104 |
+
optional: true
|
| 2105 |
+
|
| 2106 |
+
'@rollup/rollup-freebsd-x64@4.60.2':
|
| 2107 |
+
optional: true
|
| 2108 |
+
|
| 2109 |
+
'@rollup/rollup-linux-arm-gnueabihf@4.60.2':
|
| 2110 |
+
optional: true
|
| 2111 |
+
|
| 2112 |
+
'@rollup/rollup-linux-arm-musleabihf@4.60.2':
|
| 2113 |
+
optional: true
|
| 2114 |
+
|
| 2115 |
+
'@rollup/rollup-linux-arm64-gnu@4.60.2':
|
| 2116 |
+
optional: true
|
| 2117 |
+
|
| 2118 |
+
'@rollup/rollup-linux-arm64-musl@4.60.2':
|
| 2119 |
+
optional: true
|
| 2120 |
+
|
| 2121 |
+
'@rollup/rollup-linux-loong64-gnu@4.60.2':
|
| 2122 |
+
optional: true
|
| 2123 |
+
|
| 2124 |
+
'@rollup/rollup-linux-loong64-musl@4.60.2':
|
| 2125 |
+
optional: true
|
| 2126 |
+
|
| 2127 |
+
'@rollup/rollup-linux-ppc64-gnu@4.60.2':
|
| 2128 |
+
optional: true
|
| 2129 |
+
|
| 2130 |
+
'@rollup/rollup-linux-ppc64-musl@4.60.2':
|
| 2131 |
+
optional: true
|
| 2132 |
+
|
| 2133 |
+
'@rollup/rollup-linux-riscv64-gnu@4.60.2':
|
| 2134 |
+
optional: true
|
| 2135 |
+
|
| 2136 |
+
'@rollup/rollup-linux-riscv64-musl@4.60.2':
|
| 2137 |
+
optional: true
|
| 2138 |
+
|
| 2139 |
+
'@rollup/rollup-linux-s390x-gnu@4.60.2':
|
| 2140 |
+
optional: true
|
| 2141 |
+
|
| 2142 |
+
'@rollup/rollup-linux-x64-gnu@4.60.2':
|
| 2143 |
+
optional: true
|
| 2144 |
+
|
| 2145 |
+
'@rollup/rollup-linux-x64-musl@4.60.2':
|
| 2146 |
+
optional: true
|
| 2147 |
+
|
| 2148 |
+
'@rollup/rollup-openbsd-x64@4.60.2':
|
| 2149 |
+
optional: true
|
| 2150 |
+
|
| 2151 |
+
'@rollup/rollup-openharmony-arm64@4.60.2':
|
| 2152 |
+
optional: true
|
| 2153 |
+
|
| 2154 |
+
'@rollup/rollup-win32-arm64-msvc@4.60.2':
|
| 2155 |
+
optional: true
|
| 2156 |
+
|
| 2157 |
+
'@rollup/rollup-win32-ia32-msvc@4.60.2':
|
| 2158 |
+
optional: true
|
| 2159 |
+
|
| 2160 |
+
'@rollup/rollup-win32-x64-gnu@4.60.2':
|
| 2161 |
+
optional: true
|
| 2162 |
+
|
| 2163 |
+
'@rollup/rollup-win32-x64-msvc@4.60.2':
|
| 2164 |
+
optional: true
|
| 2165 |
+
|
| 2166 |
+
'@scarf/scarf@1.4.0': {}
|
| 2167 |
+
|
| 2168 |
+
'@tailwindcss/node@4.2.4':
|
| 2169 |
+
dependencies:
|
| 2170 |
+
'@jridgewell/remapping': 2.3.5
|
| 2171 |
+
enhanced-resolve: 5.21.0
|
| 2172 |
+
jiti: 2.6.1
|
| 2173 |
+
lightningcss: 1.32.0
|
| 2174 |
+
magic-string: 0.30.21
|
| 2175 |
+
source-map-js: 1.2.1
|
| 2176 |
+
tailwindcss: 4.2.4
|
| 2177 |
+
|
| 2178 |
+
'@tailwindcss/oxide-android-arm64@4.2.4':
|
| 2179 |
+
optional: true
|
| 2180 |
+
|
| 2181 |
+
'@tailwindcss/oxide-darwin-arm64@4.2.4':
|
| 2182 |
+
optional: true
|
| 2183 |
+
|
| 2184 |
+
'@tailwindcss/oxide-darwin-x64@4.2.4':
|
| 2185 |
+
optional: true
|
| 2186 |
+
|
| 2187 |
+
'@tailwindcss/oxide-freebsd-x64@4.2.4':
|
| 2188 |
+
optional: true
|
| 2189 |
+
|
| 2190 |
+
'@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4':
|
| 2191 |
+
optional: true
|
| 2192 |
+
|
| 2193 |
+
'@tailwindcss/oxide-linux-arm64-gnu@4.2.4':
|
| 2194 |
+
optional: true
|
| 2195 |
+
|
| 2196 |
+
'@tailwindcss/oxide-linux-arm64-musl@4.2.4':
|
| 2197 |
+
optional: true
|
| 2198 |
+
|
| 2199 |
+
'@tailwindcss/oxide-linux-x64-gnu@4.2.4':
|
| 2200 |
+
optional: true
|
| 2201 |
+
|
| 2202 |
+
'@tailwindcss/oxide-linux-x64-musl@4.2.4':
|
| 2203 |
+
optional: true
|
| 2204 |
+
|
| 2205 |
+
'@tailwindcss/oxide-wasm32-wasi@4.2.4':
|
| 2206 |
+
optional: true
|
| 2207 |
+
|
| 2208 |
+
'@tailwindcss/oxide-win32-arm64-msvc@4.2.4':
|
| 2209 |
+
optional: true
|
| 2210 |
+
|
| 2211 |
+
'@tailwindcss/oxide-win32-x64-msvc@4.2.4':
|
| 2212 |
+
optional: true
|
| 2213 |
+
|
| 2214 |
+
'@tailwindcss/oxide@4.2.4':
|
| 2215 |
+
optionalDependencies:
|
| 2216 |
+
'@tailwindcss/oxide-android-arm64': 4.2.4
|
| 2217 |
+
'@tailwindcss/oxide-darwin-arm64': 4.2.4
|
| 2218 |
+
'@tailwindcss/oxide-darwin-x64': 4.2.4
|
| 2219 |
+
'@tailwindcss/oxide-freebsd-x64': 4.2.4
|
| 2220 |
+
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.4
|
| 2221 |
+
'@tailwindcss/oxide-linux-arm64-gnu': 4.2.4
|
| 2222 |
+
'@tailwindcss/oxide-linux-arm64-musl': 4.2.4
|
| 2223 |
+
'@tailwindcss/oxide-linux-x64-gnu': 4.2.4
|
| 2224 |
+
'@tailwindcss/oxide-linux-x64-musl': 4.2.4
|
| 2225 |
+
'@tailwindcss/oxide-wasm32-wasi': 4.2.4
|
| 2226 |
+
'@tailwindcss/oxide-win32-arm64-msvc': 4.2.4
|
| 2227 |
+
'@tailwindcss/oxide-win32-x64-msvc': 4.2.4
|
| 2228 |
+
|
| 2229 |
+
'@tailwindcss/vite@4.2.4(vite@6.4.2(jiti@2.6.1)(lightningcss@1.32.0))':
|
| 2230 |
+
dependencies:
|
| 2231 |
+
'@tailwindcss/node': 4.2.4
|
| 2232 |
+
'@tailwindcss/oxide': 4.2.4
|
| 2233 |
+
tailwindcss: 4.2.4
|
| 2234 |
+
vite: 6.4.2(jiti@2.6.1)(lightningcss@1.32.0)
|
| 2235 |
+
|
| 2236 |
+
'@types/babel__core@7.20.5':
|
| 2237 |
+
dependencies:
|
| 2238 |
+
'@babel/parser': 7.29.2
|
| 2239 |
+
'@babel/types': 7.29.0
|
| 2240 |
+
'@types/babel__generator': 7.27.0
|
| 2241 |
+
'@types/babel__template': 7.4.4
|
| 2242 |
+
'@types/babel__traverse': 7.28.0
|
| 2243 |
+
|
| 2244 |
+
'@types/babel__generator@7.27.0':
|
| 2245 |
+
dependencies:
|
| 2246 |
+
'@babel/types': 7.29.0
|
| 2247 |
+
|
| 2248 |
+
'@types/babel__template@7.4.4':
|
| 2249 |
+
dependencies:
|
| 2250 |
+
'@babel/parser': 7.29.2
|
| 2251 |
+
'@babel/types': 7.29.0
|
| 2252 |
+
|
| 2253 |
+
'@types/babel__traverse@7.28.0':
|
| 2254 |
+
dependencies:
|
| 2255 |
+
'@babel/types': 7.29.0
|
| 2256 |
+
|
| 2257 |
+
'@types/estree@1.0.8': {}
|
| 2258 |
+
|
| 2259 |
+
'@types/react-dom@19.2.3(@types/react@19.2.14)':
|
| 2260 |
+
dependencies:
|
| 2261 |
+
'@types/react': 19.2.14
|
| 2262 |
+
|
| 2263 |
+
'@types/react@19.2.14':
|
| 2264 |
+
dependencies:
|
| 2265 |
+
csstype: 3.2.3
|
| 2266 |
+
|
| 2267 |
+
'@vitejs/plugin-react@4.7.0(vite@6.4.2(jiti@2.6.1)(lightningcss@1.32.0))':
|
| 2268 |
+
dependencies:
|
| 2269 |
+
'@babel/core': 7.29.0
|
| 2270 |
+
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
|
| 2271 |
+
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
|
| 2272 |
+
'@rolldown/pluginutils': 1.0.0-beta.27
|
| 2273 |
+
'@types/babel__core': 7.20.5
|
| 2274 |
+
react-refresh: 0.17.0
|
| 2275 |
+
vite: 6.4.2(jiti@2.6.1)(lightningcss@1.32.0)
|
| 2276 |
+
transitivePeerDependencies:
|
| 2277 |
+
- supports-color
|
| 2278 |
+
|
| 2279 |
+
aria-hidden@1.2.6:
|
| 2280 |
+
dependencies:
|
| 2281 |
+
tslib: 2.8.1
|
| 2282 |
+
|
| 2283 |
+
baseline-browser-mapping@2.10.23: {}
|
| 2284 |
+
|
| 2285 |
+
browserslist@4.28.2:
|
| 2286 |
+
dependencies:
|
| 2287 |
+
baseline-browser-mapping: 2.10.23
|
| 2288 |
+
caniuse-lite: 1.0.30001791
|
| 2289 |
+
electron-to-chromium: 1.5.344
|
| 2290 |
+
node-releases: 2.0.38
|
| 2291 |
+
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
| 2292 |
+
|
| 2293 |
+
caniuse-lite@1.0.30001791: {}
|
| 2294 |
+
|
| 2295 |
+
class-variance-authority@0.7.1:
|
| 2296 |
+
dependencies:
|
| 2297 |
+
clsx: 2.1.1
|
| 2298 |
+
|
| 2299 |
+
clsx@2.1.1: {}
|
| 2300 |
+
|
| 2301 |
+
codemirror@6.0.2:
|
| 2302 |
+
dependencies:
|
| 2303 |
+
'@codemirror/autocomplete': 6.20.1
|
| 2304 |
+
'@codemirror/commands': 6.10.3
|
| 2305 |
+
'@codemirror/language': 6.12.3
|
| 2306 |
+
'@codemirror/lint': 6.9.5
|
| 2307 |
+
'@codemirror/search': 6.7.0
|
| 2308 |
+
'@codemirror/state': 6.6.0
|
| 2309 |
+
'@codemirror/view': 6.41.1
|
| 2310 |
+
|
| 2311 |
+
convert-source-map@2.0.0: {}
|
| 2312 |
+
|
| 2313 |
+
crelt@1.0.6: {}
|
| 2314 |
+
|
| 2315 |
+
csstype@3.2.3: {}
|
| 2316 |
+
|
| 2317 |
+
debug@4.4.3:
|
| 2318 |
+
dependencies:
|
| 2319 |
+
ms: 2.1.3
|
| 2320 |
+
|
| 2321 |
+
detect-libc@2.1.2: {}
|
| 2322 |
+
|
| 2323 |
+
detect-node-es@1.1.0: {}
|
| 2324 |
+
|
| 2325 |
+
electron-to-chromium@1.5.344: {}
|
| 2326 |
+
|
| 2327 |
+
enhanced-resolve@5.21.0:
|
| 2328 |
+
dependencies:
|
| 2329 |
+
graceful-fs: 4.2.11
|
| 2330 |
+
tapable: 2.3.3
|
| 2331 |
+
|
| 2332 |
+
esbuild@0.25.12:
|
| 2333 |
+
optionalDependencies:
|
| 2334 |
+
'@esbuild/aix-ppc64': 0.25.12
|
| 2335 |
+
'@esbuild/android-arm': 0.25.12
|
| 2336 |
+
'@esbuild/android-arm64': 0.25.12
|
| 2337 |
+
'@esbuild/android-x64': 0.25.12
|
| 2338 |
+
'@esbuild/darwin-arm64': 0.25.12
|
| 2339 |
+
'@esbuild/darwin-x64': 0.25.12
|
| 2340 |
+
'@esbuild/freebsd-arm64': 0.25.12
|
| 2341 |
+
'@esbuild/freebsd-x64': 0.25.12
|
| 2342 |
+
'@esbuild/linux-arm': 0.25.12
|
| 2343 |
+
'@esbuild/linux-arm64': 0.25.12
|
| 2344 |
+
'@esbuild/linux-ia32': 0.25.12
|
| 2345 |
+
'@esbuild/linux-loong64': 0.25.12
|
| 2346 |
+
'@esbuild/linux-mips64el': 0.25.12
|
| 2347 |
+
'@esbuild/linux-ppc64': 0.25.12
|
| 2348 |
+
'@esbuild/linux-riscv64': 0.25.12
|
| 2349 |
+
'@esbuild/linux-s390x': 0.25.12
|
| 2350 |
+
'@esbuild/linux-x64': 0.25.12
|
| 2351 |
+
'@esbuild/netbsd-arm64': 0.25.12
|
| 2352 |
+
'@esbuild/netbsd-x64': 0.25.12
|
| 2353 |
+
'@esbuild/openbsd-arm64': 0.25.12
|
| 2354 |
+
'@esbuild/openbsd-x64': 0.25.12
|
| 2355 |
+
'@esbuild/openharmony-arm64': 0.25.12
|
| 2356 |
+
'@esbuild/sunos-x64': 0.25.12
|
| 2357 |
+
'@esbuild/win32-arm64': 0.25.12
|
| 2358 |
+
'@esbuild/win32-ia32': 0.25.12
|
| 2359 |
+
'@esbuild/win32-x64': 0.25.12
|
| 2360 |
+
|
| 2361 |
+
escalade@3.2.0: {}
|
| 2362 |
+
|
| 2363 |
+
fdir@6.5.0(picomatch@4.0.4):
|
| 2364 |
+
optionalDependencies:
|
| 2365 |
+
picomatch: 4.0.4
|
| 2366 |
+
|
| 2367 |
+
framer-motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
|
| 2368 |
+
dependencies:
|
| 2369 |
+
motion-dom: 12.38.0
|
| 2370 |
+
motion-utils: 12.36.0
|
| 2371 |
+
tslib: 2.8.1
|
| 2372 |
+
optionalDependencies:
|
| 2373 |
+
react: 19.2.5
|
| 2374 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2375 |
+
|
| 2376 |
+
fsevents@2.3.3:
|
| 2377 |
+
optional: true
|
| 2378 |
+
|
| 2379 |
+
gensync@1.0.0-beta.2: {}
|
| 2380 |
+
|
| 2381 |
+
get-nonce@1.0.1: {}
|
| 2382 |
+
|
| 2383 |
+
graceful-fs@4.2.11: {}
|
| 2384 |
+
|
| 2385 |
+
jiti@2.6.1: {}
|
| 2386 |
+
|
| 2387 |
+
js-tokens@4.0.0: {}
|
| 2388 |
+
|
| 2389 |
+
jsesc@3.1.0: {}
|
| 2390 |
+
|
| 2391 |
+
json5@2.2.3: {}
|
| 2392 |
+
|
| 2393 |
+
lightningcss-android-arm64@1.32.0:
|
| 2394 |
+
optional: true
|
| 2395 |
+
|
| 2396 |
+
lightningcss-darwin-arm64@1.32.0:
|
| 2397 |
+
optional: true
|
| 2398 |
+
|
| 2399 |
+
lightningcss-darwin-x64@1.32.0:
|
| 2400 |
+
optional: true
|
| 2401 |
+
|
| 2402 |
+
lightningcss-freebsd-x64@1.32.0:
|
| 2403 |
+
optional: true
|
| 2404 |
+
|
| 2405 |
+
lightningcss-linux-arm-gnueabihf@1.32.0:
|
| 2406 |
+
optional: true
|
| 2407 |
+
|
| 2408 |
+
lightningcss-linux-arm64-gnu@1.32.0:
|
| 2409 |
+
optional: true
|
| 2410 |
+
|
| 2411 |
+
lightningcss-linux-arm64-musl@1.32.0:
|
| 2412 |
+
optional: true
|
| 2413 |
+
|
| 2414 |
+
lightningcss-linux-x64-gnu@1.32.0:
|
| 2415 |
+
optional: true
|
| 2416 |
+
|
| 2417 |
+
lightningcss-linux-x64-musl@1.32.0:
|
| 2418 |
+
optional: true
|
| 2419 |
+
|
| 2420 |
+
lightningcss-win32-arm64-msvc@1.32.0:
|
| 2421 |
+
optional: true
|
| 2422 |
+
|
| 2423 |
+
lightningcss-win32-x64-msvc@1.32.0:
|
| 2424 |
+
optional: true
|
| 2425 |
+
|
| 2426 |
+
lightningcss@1.32.0:
|
| 2427 |
+
dependencies:
|
| 2428 |
+
detect-libc: 2.1.2
|
| 2429 |
+
optionalDependencies:
|
| 2430 |
+
lightningcss-android-arm64: 1.32.0
|
| 2431 |
+
lightningcss-darwin-arm64: 1.32.0
|
| 2432 |
+
lightningcss-darwin-x64: 1.32.0
|
| 2433 |
+
lightningcss-freebsd-x64: 1.32.0
|
| 2434 |
+
lightningcss-linux-arm-gnueabihf: 1.32.0
|
| 2435 |
+
lightningcss-linux-arm64-gnu: 1.32.0
|
| 2436 |
+
lightningcss-linux-arm64-musl: 1.32.0
|
| 2437 |
+
lightningcss-linux-x64-gnu: 1.32.0
|
| 2438 |
+
lightningcss-linux-x64-musl: 1.32.0
|
| 2439 |
+
lightningcss-win32-arm64-msvc: 1.32.0
|
| 2440 |
+
lightningcss-win32-x64-msvc: 1.32.0
|
| 2441 |
+
|
| 2442 |
+
lru-cache@5.1.1:
|
| 2443 |
+
dependencies:
|
| 2444 |
+
yallist: 3.1.1
|
| 2445 |
+
|
| 2446 |
+
lucide-react@0.453.0(react@19.2.5):
|
| 2447 |
+
dependencies:
|
| 2448 |
+
react: 19.2.5
|
| 2449 |
+
|
| 2450 |
+
magic-string@0.30.21:
|
| 2451 |
+
dependencies:
|
| 2452 |
+
'@jridgewell/sourcemap-codec': 1.5.5
|
| 2453 |
+
|
| 2454 |
+
mitt@3.0.1: {}
|
| 2455 |
+
|
| 2456 |
+
motion-dom@12.38.0:
|
| 2457 |
+
dependencies:
|
| 2458 |
+
motion-utils: 12.36.0
|
| 2459 |
+
|
| 2460 |
+
motion-utils@12.36.0: {}
|
| 2461 |
+
|
| 2462 |
+
ms@2.1.3: {}
|
| 2463 |
+
|
| 2464 |
+
nanoid@3.3.11: {}
|
| 2465 |
+
|
| 2466 |
+
node-releases@2.0.38: {}
|
| 2467 |
+
|
| 2468 |
+
picocolors@1.1.1: {}
|
| 2469 |
+
|
| 2470 |
+
picomatch@4.0.4: {}
|
| 2471 |
+
|
| 2472 |
+
postcss@8.5.12:
|
| 2473 |
+
dependencies:
|
| 2474 |
+
nanoid: 3.3.11
|
| 2475 |
+
picocolors: 1.1.1
|
| 2476 |
+
source-map-js: 1.2.1
|
| 2477 |
+
|
| 2478 |
+
react-dom@19.2.5(react@19.2.5):
|
| 2479 |
+
dependencies:
|
| 2480 |
+
react: 19.2.5
|
| 2481 |
+
scheduler: 0.27.0
|
| 2482 |
+
|
| 2483 |
+
react-refresh@0.17.0: {}
|
| 2484 |
+
|
| 2485 |
+
react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.5):
|
| 2486 |
+
dependencies:
|
| 2487 |
+
react: 19.2.5
|
| 2488 |
+
react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 2489 |
+
tslib: 2.8.1
|
| 2490 |
+
optionalDependencies:
|
| 2491 |
+
'@types/react': 19.2.14
|
| 2492 |
+
|
| 2493 |
+
react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.5):
|
| 2494 |
+
dependencies:
|
| 2495 |
+
react: 19.2.5
|
| 2496 |
+
react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.5)
|
| 2497 |
+
react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5)
|
| 2498 |
+
tslib: 2.8.1
|
| 2499 |
+
use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.5)
|
| 2500 |
+
use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.5)
|
| 2501 |
+
optionalDependencies:
|
| 2502 |
+
'@types/react': 19.2.14
|
| 2503 |
+
|
| 2504 |
+
react-resizable-panels@3.0.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
|
| 2505 |
+
dependencies:
|
| 2506 |
+
react: 19.2.5
|
| 2507 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2508 |
+
|
| 2509 |
+
react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.5):
|
| 2510 |
+
dependencies:
|
| 2511 |
+
get-nonce: 1.0.1
|
| 2512 |
+
react: 19.2.5
|
| 2513 |
+
tslib: 2.8.1
|
| 2514 |
+
optionalDependencies:
|
| 2515 |
+
'@types/react': 19.2.14
|
| 2516 |
+
|
| 2517 |
+
react@19.2.5: {}
|
| 2518 |
+
|
| 2519 |
+
regexparam@3.0.0: {}
|
| 2520 |
+
|
| 2521 |
+
rollup@4.60.2:
|
| 2522 |
+
dependencies:
|
| 2523 |
+
'@types/estree': 1.0.8
|
| 2524 |
+
optionalDependencies:
|
| 2525 |
+
'@rollup/rollup-android-arm-eabi': 4.60.2
|
| 2526 |
+
'@rollup/rollup-android-arm64': 4.60.2
|
| 2527 |
+
'@rollup/rollup-darwin-arm64': 4.60.2
|
| 2528 |
+
'@rollup/rollup-darwin-x64': 4.60.2
|
| 2529 |
+
'@rollup/rollup-freebsd-arm64': 4.60.2
|
| 2530 |
+
'@rollup/rollup-freebsd-x64': 4.60.2
|
| 2531 |
+
'@rollup/rollup-linux-arm-gnueabihf': 4.60.2
|
| 2532 |
+
'@rollup/rollup-linux-arm-musleabihf': 4.60.2
|
| 2533 |
+
'@rollup/rollup-linux-arm64-gnu': 4.60.2
|
| 2534 |
+
'@rollup/rollup-linux-arm64-musl': 4.60.2
|
| 2535 |
+
'@rollup/rollup-linux-loong64-gnu': 4.60.2
|
| 2536 |
+
'@rollup/rollup-linux-loong64-musl': 4.60.2
|
| 2537 |
+
'@rollup/rollup-linux-ppc64-gnu': 4.60.2
|
| 2538 |
+
'@rollup/rollup-linux-ppc64-musl': 4.60.2
|
| 2539 |
+
'@rollup/rollup-linux-riscv64-gnu': 4.60.2
|
| 2540 |
+
'@rollup/rollup-linux-riscv64-musl': 4.60.2
|
| 2541 |
+
'@rollup/rollup-linux-s390x-gnu': 4.60.2
|
| 2542 |
+
'@rollup/rollup-linux-x64-gnu': 4.60.2
|
| 2543 |
+
'@rollup/rollup-linux-x64-musl': 4.60.2
|
| 2544 |
+
'@rollup/rollup-openbsd-x64': 4.60.2
|
| 2545 |
+
'@rollup/rollup-openharmony-arm64': 4.60.2
|
| 2546 |
+
'@rollup/rollup-win32-arm64-msvc': 4.60.2
|
| 2547 |
+
'@rollup/rollup-win32-ia32-msvc': 4.60.2
|
| 2548 |
+
'@rollup/rollup-win32-x64-gnu': 4.60.2
|
| 2549 |
+
'@rollup/rollup-win32-x64-msvc': 4.60.2
|
| 2550 |
+
fsevents: 2.3.3
|
| 2551 |
+
|
| 2552 |
+
scheduler@0.27.0: {}
|
| 2553 |
+
|
| 2554 |
+
semver@6.3.1: {}
|
| 2555 |
+
|
| 2556 |
+
sonner@2.0.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
|
| 2557 |
+
dependencies:
|
| 2558 |
+
react: 19.2.5
|
| 2559 |
+
react-dom: 19.2.5(react@19.2.5)
|
| 2560 |
+
|
| 2561 |
+
source-map-js@1.2.1: {}
|
| 2562 |
+
|
| 2563 |
+
style-mod@4.1.3: {}
|
| 2564 |
+
|
| 2565 |
+
swagger-ui-dist@5.32.5:
|
| 2566 |
+
dependencies:
|
| 2567 |
+
'@scarf/scarf': 1.4.0
|
| 2568 |
+
|
| 2569 |
+
tailwind-merge@3.5.0: {}
|
| 2570 |
+
|
| 2571 |
+
tailwindcss@4.2.4: {}
|
| 2572 |
+
|
| 2573 |
+
tapable@2.3.3: {}
|
| 2574 |
+
|
| 2575 |
+
tinyglobby@0.2.16:
|
| 2576 |
+
dependencies:
|
| 2577 |
+
fdir: 6.5.0(picomatch@4.0.4)
|
| 2578 |
+
picomatch: 4.0.4
|
| 2579 |
+
|
| 2580 |
+
tslib@2.8.1: {}
|
| 2581 |
+
|
| 2582 |
+
typescript@5.9.3: {}
|
| 2583 |
+
|
| 2584 |
+
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
| 2585 |
+
dependencies:
|
| 2586 |
+
browserslist: 4.28.2
|
| 2587 |
+
escalade: 3.2.0
|
| 2588 |
+
picocolors: 1.1.1
|
| 2589 |
+
|
| 2590 |
+
use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.5):
|
| 2591 |
+
dependencies:
|
| 2592 |
+
react: 19.2.5
|
| 2593 |
+
tslib: 2.8.1
|
| 2594 |
+
optionalDependencies:
|
| 2595 |
+
'@types/react': 19.2.14
|
| 2596 |
+
|
| 2597 |
+
use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.5):
|
| 2598 |
+
dependencies:
|
| 2599 |
+
detect-node-es: 1.1.0
|
| 2600 |
+
react: 19.2.5
|
| 2601 |
+
tslib: 2.8.1
|
| 2602 |
+
optionalDependencies:
|
| 2603 |
+
'@types/react': 19.2.14
|
| 2604 |
+
|
| 2605 |
+
use-sync-external-store@1.6.0(react@19.2.5):
|
| 2606 |
+
dependencies:
|
| 2607 |
+
react: 19.2.5
|
| 2608 |
+
|
| 2609 |
+
vite@6.4.2(jiti@2.6.1)(lightningcss@1.32.0):
|
| 2610 |
+
dependencies:
|
| 2611 |
+
esbuild: 0.25.12
|
| 2612 |
+
fdir: 6.5.0(picomatch@4.0.4)
|
| 2613 |
+
picomatch: 4.0.4
|
| 2614 |
+
postcss: 8.5.12
|
| 2615 |
+
rollup: 4.60.2
|
| 2616 |
+
tinyglobby: 0.2.16
|
| 2617 |
+
optionalDependencies:
|
| 2618 |
+
fsevents: 2.3.3
|
| 2619 |
+
jiti: 2.6.1
|
| 2620 |
+
lightningcss: 1.32.0
|
| 2621 |
+
|
| 2622 |
+
w3c-keyname@2.2.8: {}
|
| 2623 |
+
|
| 2624 |
+
wouter@3.9.0(react@19.2.5):
|
| 2625 |
+
dependencies:
|
| 2626 |
+
mitt: 3.0.1
|
| 2627 |
+
react: 19.2.5
|
| 2628 |
+
regexparam: 3.0.0
|
| 2629 |
+
use-sync-external-store: 1.6.0(react@19.2.5)
|
| 2630 |
+
|
| 2631 |
+
yallist@3.1.1: {}
|
frontend/src/App.tsx
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Route, Switch } from "wouter";
|
| 2 |
+
import { Toaster } from "sonner";
|
| 3 |
+
import Analyzer from "./pages/Analyzer";
|
| 4 |
+
import SwaggerPage from "./pages/SwaggerPage";
|
| 5 |
+
import NotFound from "./pages/NotFound";
|
| 6 |
+
|
| 7 |
+
export default function App() {
|
| 8 |
+
return (
|
| 9 |
+
<>
|
| 10 |
+
<Toaster
|
| 11 |
+
theme="dark"
|
| 12 |
+
position="bottom-right"
|
| 13 |
+
toastOptions={{
|
| 14 |
+
style: {
|
| 15 |
+
background: "var(--bg-elevated)",
|
| 16 |
+
border: "1px solid var(--border)",
|
| 17 |
+
color: "var(--text-primary)",
|
| 18 |
+
fontFamily: "var(--font-sans)",
|
| 19 |
+
},
|
| 20 |
+
}}
|
| 21 |
+
/>
|
| 22 |
+
<Switch>
|
| 23 |
+
<Route path="/" component={Analyzer} />
|
| 24 |
+
<Route path="/swagger" component={SwaggerPage} />
|
| 25 |
+
<Route component={NotFound} />
|
| 26 |
+
</Switch>
|
| 27 |
+
</>
|
| 28 |
+
);
|
| 29 |
+
}
|
frontend/src/components/AstTreeView.tsx
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useEffect, useMemo, useCallback } from "react";
|
| 2 |
+
import { ChevronRight, ChevronDown, Search, X } from "lucide-react";
|
| 3 |
+
import type { AstNode } from "@/lib/api";
|
| 4 |
+
|
| 5 |
+
// ── Node type → CSS class mapping ─────────────────────────────────────
|
| 6 |
+
|
| 7 |
+
function getNodeClass(type: string): string {
|
| 8 |
+
const t = type.toLowerCase();
|
| 9 |
+
if (t.includes("keyword") || t.includes("statement") || t.includes("clause")) return "ast-keyword";
|
| 10 |
+
if (t.includes("literal") || t.includes("quoted") || t.includes("string")) return "ast-literal";
|
| 11 |
+
if (t.includes("numeric") || t.includes("integer") || t.includes("float")) return "ast-numeric";
|
| 12 |
+
if (t.includes("comment")) return "ast-comment";
|
| 13 |
+
if (t.includes("function") || t.includes("func")) return "ast-function";
|
| 14 |
+
if (t.includes("operator") || t.includes("comparison") || t.includes("arithmetic")) return "ast-operator";
|
| 15 |
+
if (t.includes("type") || t.includes("datatype")) return "ast-type";
|
| 16 |
+
if (t.includes("whitespace") || t.includes("newline") || t.includes("indent")) return "ast-whitespace";
|
| 17 |
+
return "ast-default";
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
function getNodeBadgeStyle(type: string): React.CSSProperties {
|
| 21 |
+
const t = type.toLowerCase();
|
| 22 |
+
if (t.includes("keyword") || t.includes("statement") || t.includes("clause"))
|
| 23 |
+
return { background: "oklch(0.72 0.18 270 / 0.15)", color: "oklch(0.72 0.18 270)", border: "1px solid oklch(0.72 0.18 270 / 0.3)" };
|
| 24 |
+
if (t.includes("literal") || t.includes("quoted") || t.includes("string"))
|
| 25 |
+
return { background: "oklch(0.75 0.16 145 / 0.15)", color: "oklch(0.75 0.16 145)", border: "1px solid oklch(0.75 0.16 145 / 0.3)" };
|
| 26 |
+
if (t.includes("numeric") || t.includes("integer") || t.includes("float"))
|
| 27 |
+
return { background: "oklch(0.78 0.18 55 / 0.15)", color: "oklch(0.78 0.18 55)", border: "1px solid oklch(0.78 0.18 55 / 0.3)" };
|
| 28 |
+
if (t.includes("comment"))
|
| 29 |
+
return { background: "oklch(0.50 0.010 264 / 0.15)", color: "oklch(0.55 0.010 264)", border: "1px solid oklch(0.50 0.010 264 / 0.3)" };
|
| 30 |
+
if (t.includes("function") || t.includes("func"))
|
| 31 |
+
return { background: "oklch(0.78 0.16 210 / 0.15)", color: "oklch(0.78 0.16 210)", border: "1px solid oklch(0.78 0.16 210 / 0.3)" };
|
| 32 |
+
if (t.includes("operator") || t.includes("comparison"))
|
| 33 |
+
return { background: "oklch(0.82 0.010 264 / 0.10)", color: "oklch(0.82 0.010 264)", border: "1px solid oklch(0.82 0.010 264 / 0.3)" };
|
| 34 |
+
if (t.includes("whitespace") || t.includes("newline"))
|
| 35 |
+
return { background: "oklch(0.20 0.008 264 / 0.5)", color: "oklch(0.38 0.008 264)", border: "1px solid oklch(0.25 0.008 264 / 0.3)" };
|
| 36 |
+
return { background: "oklch(0.20 0.010 264 / 0.5)", color: "oklch(0.72 0.010 264)", border: "1px solid oklch(0.28 0.010 264 / 0.3)" };
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
// ── Search helpers ─────────────────────────────────────────────────────
|
| 40 |
+
|
| 41 |
+
function collectMatchIds(node: AstNode, query: string, matchIds: Set<string>, ancestorIds: Set<string>, path: string[]): boolean {
|
| 42 |
+
const q = query.toLowerCase();
|
| 43 |
+
const matches =
|
| 44 |
+
node.type.toLowerCase().includes(q) ||
|
| 45 |
+
node.name.toLowerCase().includes(q) ||
|
| 46 |
+
(node.raw?.toLowerCase().includes(q) ?? false);
|
| 47 |
+
|
| 48 |
+
let childMatched = false;
|
| 49 |
+
for (const child of node.children) {
|
| 50 |
+
if (collectMatchIds(child, query, matchIds, ancestorIds, [...path, node.id])) {
|
| 51 |
+
childMatched = true;
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
if (matches) {
|
| 56 |
+
matchIds.add(node.id);
|
| 57 |
+
path.forEach((id) => ancestorIds.add(id));
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
return matches || childMatched;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function highlightText(text: string, query: string): React.ReactNode {
|
| 64 |
+
if (!query) return text;
|
| 65 |
+
const idx = text.toLowerCase().indexOf(query.toLowerCase());
|
| 66 |
+
if (idx === -1) return text;
|
| 67 |
+
return (
|
| 68 |
+
<>
|
| 69 |
+
{text.slice(0, idx)}
|
| 70 |
+
<mark style={{ background: "oklch(0.68 0.16 210 / 0.35)", color: "inherit", borderRadius: "2px" }}>
|
| 71 |
+
{text.slice(idx, idx + query.length)}
|
| 72 |
+
</mark>
|
| 73 |
+
{text.slice(idx + query.length)}
|
| 74 |
+
</>
|
| 75 |
+
);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
// ── Single tree node ───────────────────────────────────────────────────
|
| 79 |
+
|
| 80 |
+
interface NodeProps {
|
| 81 |
+
node: AstNode;
|
| 82 |
+
depth: number;
|
| 83 |
+
query: string;
|
| 84 |
+
matchIds: Set<string>;
|
| 85 |
+
ancestorIds: Set<string>;
|
| 86 |
+
expandedIds: Set<string>;
|
| 87 |
+
onToggle: (id: string) => void;
|
| 88 |
+
onJumpToLine?: (line: number) => void;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function TreeNode({ node, depth, query, matchIds, ancestorIds, expandedIds, onToggle, onJumpToLine }: NodeProps) {
|
| 92 |
+
const isMatch = matchIds.has(node.id);
|
| 93 |
+
const isAncestor = ancestorIds.has(node.id);
|
| 94 |
+
const isExpanded = expandedIds.has(node.id);
|
| 95 |
+
const hasChildren = node.children.length > 0;
|
| 96 |
+
|
| 97 |
+
// Skip pure whitespace nodes when no search
|
| 98 |
+
const isWhitespace = node.type.toLowerCase().includes("whitespace") || node.type.toLowerCase().includes("newline");
|
| 99 |
+
if (isWhitespace && !query && !node.raw?.trim()) return null;
|
| 100 |
+
|
| 101 |
+
const indent = depth * 16;
|
| 102 |
+
const nodeClass = getNodeClass(node.type);
|
| 103 |
+
const badgeStyle = getNodeBadgeStyle(node.type);
|
| 104 |
+
|
| 105 |
+
return (
|
| 106 |
+
<div>
|
| 107 |
+
<div
|
| 108 |
+
className={`flex items-center gap-1.5 py-0.5 px-2 rounded cursor-pointer group transition-colors duration-100 ${
|
| 109 |
+
isMatch
|
| 110 |
+
? "bg-[oklch(0.68_0.16_210_/_0.12)] border border-[oklch(0.68_0.16_210_/_0.3)]"
|
| 111 |
+
: "hover:bg-[oklch(0.18_0.010_264)]"
|
| 112 |
+
}`}
|
| 113 |
+
style={{ paddingLeft: `${indent + 8}px` }}
|
| 114 |
+
onClick={() => {
|
| 115 |
+
if (hasChildren) onToggle(node.id);
|
| 116 |
+
if (node.start_line && onJumpToLine) onJumpToLine(node.start_line);
|
| 117 |
+
}}
|
| 118 |
+
>
|
| 119 |
+
{/* Expand/collapse chevron */}
|
| 120 |
+
<span className="w-4 h-4 flex items-center justify-center flex-shrink-0 text-[oklch(0.45_0.010_264)]">
|
| 121 |
+
{hasChildren ? (
|
| 122 |
+
isExpanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />
|
| 123 |
+
) : (
|
| 124 |
+
<span className="w-1 h-1 rounded-full bg-[oklch(0.30_0.010_264)]" />
|
| 125 |
+
)}
|
| 126 |
+
</span>
|
| 127 |
+
|
| 128 |
+
{/* Node type badge */}
|
| 129 |
+
<span
|
| 130 |
+
className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded flex-shrink-0"
|
| 131 |
+
style={badgeStyle}
|
| 132 |
+
>
|
| 133 |
+
{highlightText(node.type, query)}
|
| 134 |
+
</span>
|
| 135 |
+
|
| 136 |
+
{/* Raw value for leaf nodes */}
|
| 137 |
+
{node.is_leaf && node.raw && node.raw.trim() && (
|
| 138 |
+
<span className={`text-xs font-mono truncate max-w-[200px] ${nodeClass}`}>
|
| 139 |
+
{highlightText(JSON.stringify(node.raw), query)}
|
| 140 |
+
</span>
|
| 141 |
+
)}
|
| 142 |
+
|
| 143 |
+
{/* Position info */}
|
| 144 |
+
{node.start_line && (
|
| 145 |
+
<span className="text-[10px] text-[oklch(0.38_0.010_264)] ml-auto flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">
|
| 146 |
+
L{node.start_line}:{node.start_pos}
|
| 147 |
+
</span>
|
| 148 |
+
)}
|
| 149 |
+
</div>
|
| 150 |
+
|
| 151 |
+
{/* Children */}
|
| 152 |
+
{hasChildren && isExpanded && (
|
| 153 |
+
<div>
|
| 154 |
+
{node.children.map((child) => (
|
| 155 |
+
<TreeNode
|
| 156 |
+
key={child.id}
|
| 157 |
+
node={child}
|
| 158 |
+
depth={depth + 1}
|
| 159 |
+
query={query}
|
| 160 |
+
matchIds={matchIds}
|
| 161 |
+
ancestorIds={ancestorIds}
|
| 162 |
+
expandedIds={expandedIds}
|
| 163 |
+
onToggle={onToggle}
|
| 164 |
+
onJumpToLine={onJumpToLine}
|
| 165 |
+
/>
|
| 166 |
+
))}
|
| 167 |
+
</div>
|
| 168 |
+
)}
|
| 169 |
+
</div>
|
| 170 |
+
);
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
// ── Main component ─────────────────────────────────────────────────────
|
| 174 |
+
|
| 175 |
+
interface AstTreeViewProps {
|
| 176 |
+
tree: AstNode | null;
|
| 177 |
+
tokenCount?: number;
|
| 178 |
+
depth?: number;
|
| 179 |
+
onJumpToLine?: (line: number) => void;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
export function AstTreeView({ tree, tokenCount, depth, onJumpToLine }: AstTreeViewProps) {
|
| 183 |
+
const [query, setQuery] = useState("");
|
| 184 |
+
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
| 185 |
+
|
| 186 |
+
// Auto-expand root on first load
|
| 187 |
+
useEffect(() => {
|
| 188 |
+
if (tree) {
|
| 189 |
+
setExpandedIds(new Set([tree.id, ...tree.children.map((c) => c.id)]));
|
| 190 |
+
}
|
| 191 |
+
}, [tree]);
|
| 192 |
+
|
| 193 |
+
const { matchIds, ancestorIds } = useMemo(() => {
|
| 194 |
+
if (!tree || !query.trim()) return { matchIds: new Set<string>(), ancestorIds: new Set<string>() };
|
| 195 |
+
const matchIds = new Set<string>();
|
| 196 |
+
const ancestorIds = new Set<string>();
|
| 197 |
+
collectMatchIds(tree, query.trim(), matchIds, ancestorIds, []);
|
| 198 |
+
return { matchIds, ancestorIds };
|
| 199 |
+
}, [tree, query]);
|
| 200 |
+
|
| 201 |
+
// Auto-expand ancestors when searching
|
| 202 |
+
useEffect(() => {
|
| 203 |
+
if (query.trim() && ancestorIds.size > 0) {
|
| 204 |
+
setExpandedIds((prev) => {
|
| 205 |
+
const next = new Set(prev);
|
| 206 |
+
ancestorIds.forEach((id) => next.add(id));
|
| 207 |
+
matchIds.forEach((id) => next.add(id));
|
| 208 |
+
return next;
|
| 209 |
+
});
|
| 210 |
+
}
|
| 211 |
+
}, [ancestorIds, matchIds, query]);
|
| 212 |
+
|
| 213 |
+
const handleToggle = useCallback((id: string) => {
|
| 214 |
+
setExpandedIds((prev) => {
|
| 215 |
+
const next = new Set(prev);
|
| 216 |
+
if (next.has(id)) next.delete(id);
|
| 217 |
+
else next.add(id);
|
| 218 |
+
return next;
|
| 219 |
+
});
|
| 220 |
+
}, []);
|
| 221 |
+
|
| 222 |
+
const expandAll = useCallback(() => {
|
| 223 |
+
if (!tree) return;
|
| 224 |
+
const ids = new Set<string>();
|
| 225 |
+
const collect = (n: AstNode) => { ids.add(n.id); n.children.forEach(collect); };
|
| 226 |
+
collect(tree);
|
| 227 |
+
setExpandedIds(ids);
|
| 228 |
+
}, [tree]);
|
| 229 |
+
|
| 230 |
+
const collapseAll = useCallback(() => {
|
| 231 |
+
if (!tree) return;
|
| 232 |
+
setExpandedIds(new Set([tree.id]));
|
| 233 |
+
}, [tree]);
|
| 234 |
+
|
| 235 |
+
if (!tree) {
|
| 236 |
+
return (
|
| 237 |
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-[oklch(0.45_0.010_264)]">
|
| 238 |
+
<div className="text-4xl opacity-30">⟨/⟩</div>
|
| 239 |
+
<p className="text-sm">Run analysis to view the AST</p>
|
| 240 |
+
</div>
|
| 241 |
+
);
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
return (
|
| 245 |
+
<div className="flex flex-col h-full">
|
| 246 |
+
{/* Toolbar */}
|
| 247 |
+
<div className="flex items-center gap-2 px-3 py-2 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0">
|
| 248 |
+
<div className="relative flex-1">
|
| 249 |
+
<Search size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-[oklch(0.45_0.010_264)]" />
|
| 250 |
+
<input
|
| 251 |
+
value={query}
|
| 252 |
+
onChange={(e) => setQuery(e.target.value)}
|
| 253 |
+
placeholder="Filter by node type…"
|
| 254 |
+
className="h-7 pl-7 pr-7 text-xs w-full rounded"
|
| 255 |
+
style={{ background: "var(--bg-elevated)", border: "1px solid var(--border)", color: "var(--text-primary)", fontFamily: "var(--font-mono)", outline: "none", paddingLeft: "28px", paddingRight: "28px" }}
|
| 256 |
+
/>
|
| 257 |
+
{query && (
|
| 258 |
+
<button
|
| 259 |
+
onClick={() => setQuery("")}
|
| 260 |
+
className="absolute right-2 top-1/2 -translate-y-1/2 text-[oklch(0.45_0.010_264)] hover:text-foreground"
|
| 261 |
+
>
|
| 262 |
+
<X size={12} />
|
| 263 |
+
</button>
|
| 264 |
+
)}
|
| 265 |
+
</div>
|
| 266 |
+
<button onClick={expandAll} className="h-7 px-2 text-xs rounded transition-colors" style={{ color: "var(--text-secondary)", background: "transparent" }}>Expand</button>
|
| 267 |
+
<button onClick={collapseAll} className="h-7 px-2 text-xs rounded transition-colors" style={{ color: "var(--text-secondary)", background: "transparent" }}>Collapse</button>
|
| 268 |
+
</div>
|
| 269 |
+
|
| 270 |
+
{/* Stats */}
|
| 271 |
+
<div className="flex items-center gap-3 px-3 py-1.5 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0">
|
| 272 |
+
<span className="text-[10px] text-[oklch(0.45_0.010_264)]">
|
| 273 |
+
<span className="text-[oklch(0.68_0.16_210)]">{tokenCount}</span> tokens
|
| 274 |
+
</span>
|
| 275 |
+
<span className="text-[10px] text-[oklch(0.45_0.010_264)]">
|
| 276 |
+
depth <span className="text-[oklch(0.68_0.16_210)]">{depth}</span>
|
| 277 |
+
</span>
|
| 278 |
+
{query && matchIds.size > 0 && (
|
| 279 |
+
<span className="text-[10px] text-[oklch(0.75_0.16_145)]">
|
| 280 |
+
{matchIds.size} match{matchIds.size !== 1 ? "es" : ""}
|
| 281 |
+
</span>
|
| 282 |
+
)}
|
| 283 |
+
{query && matchIds.size === 0 && (
|
| 284 |
+
<span className="text-[10px] text-[oklch(0.60_0.20_25)]">No matches</span>
|
| 285 |
+
)}
|
| 286 |
+
</div>
|
| 287 |
+
|
| 288 |
+
{/* Tree */}
|
| 289 |
+
<div className="flex-1 overflow-auto py-1 text-xs">
|
| 290 |
+
<TreeNode
|
| 291 |
+
node={tree}
|
| 292 |
+
depth={0}
|
| 293 |
+
query={query}
|
| 294 |
+
matchIds={matchIds}
|
| 295 |
+
ancestorIds={ancestorIds}
|
| 296 |
+
expandedIds={expandedIds}
|
| 297 |
+
onToggle={handleToggle}
|
| 298 |
+
onJumpToLine={onJumpToLine}
|
| 299 |
+
/>
|
| 300 |
+
</div>
|
| 301 |
+
</div>
|
| 302 |
+
);
|
| 303 |
+
}
|
frontend/src/components/FormatterPanel.tsx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from "react";
|
| 2 |
+
import { Copy, Check, ArrowLeftRight, Wand2 } from "lucide-react";
|
| 3 |
+
import { toast } from "sonner";
|
| 4 |
+
import type { FormatResult } from "@/lib/api";
|
| 5 |
+
|
| 6 |
+
interface FormatterPanelProps {
|
| 7 |
+
result: FormatResult | null;
|
| 8 |
+
onApplyToEditor?: (sql: string) => void;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
export function FormatterPanel({ result, onApplyToEditor }: FormatterPanelProps) {
|
| 12 |
+
const [copied, setCopied] = useState(false);
|
| 13 |
+
|
| 14 |
+
const handleCopy = async () => {
|
| 15 |
+
if (!result) return;
|
| 16 |
+
await navigator.clipboard.writeText(result.formatted);
|
| 17 |
+
setCopied(true);
|
| 18 |
+
toast.success("Copied to clipboard");
|
| 19 |
+
setTimeout(() => setCopied(false), 2000);
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
const handleApply = () => {
|
| 23 |
+
if (!result) return;
|
| 24 |
+
onApplyToEditor?.(result.formatted);
|
| 25 |
+
toast.success("Applied to editor");
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
if (!result) {
|
| 29 |
+
return (
|
| 30 |
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-[oklch(0.45_0.010_264)]">
|
| 31 |
+
<Wand2 size={32} className="opacity-30" />
|
| 32 |
+
<p className="text-sm">Run analysis to see formatted SQL</p>
|
| 33 |
+
</div>
|
| 34 |
+
);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
return (
|
| 38 |
+
<div className="flex flex-col h-full">
|
| 39 |
+
{/* Toolbar */}
|
| 40 |
+
<div className="flex items-center gap-2 px-3 py-2 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0">
|
| 41 |
+
<div className="flex items-center gap-2 flex-1">
|
| 42 |
+
{result.changed ? (
|
| 43 |
+
<div className="flex items-center gap-1.5 text-xs" style={{ color: "oklch(0.72 0.17 160)" }}>
|
| 44 |
+
<Wand2 size={13} />
|
| 45 |
+
<span className="font-medium">{result.fixes_applied} fix{result.fixes_applied !== 1 ? "es" : ""} applied</span>
|
| 46 |
+
</div>
|
| 47 |
+
) : (
|
| 48 |
+
<div className="flex items-center gap-1.5 text-xs text-[oklch(0.55_0.010_264)]">
|
| 49 |
+
<Check size={13} />
|
| 50 |
+
<span>No formatting changes needed</span>
|
| 51 |
+
</div>
|
| 52 |
+
)}
|
| 53 |
+
<span className="text-[10px] text-[oklch(0.38_0.010_264)] font-mono ml-2">
|
| 54 |
+
dialect: {result.dialect}
|
| 55 |
+
</span>
|
| 56 |
+
</div>
|
| 57 |
+
|
| 58 |
+
<button
|
| 59 |
+
onClick={handleCopy}
|
| 60 |
+
className="flex items-center gap-1.5 h-7 px-2 text-xs rounded transition-colors"
|
| 61 |
+
style={{ color: "var(--text-secondary)", background: "transparent" }}
|
| 62 |
+
>
|
| 63 |
+
{copied ? <Check size={12} /> : <Copy size={12} />}
|
| 64 |
+
{copied ? "Copied" : "Copy"}
|
| 65 |
+
</button>
|
| 66 |
+
|
| 67 |
+
<button
|
| 68 |
+
onClick={handleApply}
|
| 69 |
+
className="flex items-center gap-1.5 h-7 px-2 text-xs font-semibold rounded"
|
| 70 |
+
style={{ background: "var(--accent-blue)", color: "var(--bg-base)" }}
|
| 71 |
+
>
|
| 72 |
+
<ArrowLeftRight size={12} />
|
| 73 |
+
Apply to Editor
|
| 74 |
+
</button>
|
| 75 |
+
</div>
|
| 76 |
+
|
| 77 |
+
{/* Diff indicator */}
|
| 78 |
+
{result.changed && (
|
| 79 |
+
<div className="flex items-center gap-2 px-3 py-1.5 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0 bg-[oklch(0.72_0.17_160_/_0.05)]">
|
| 80 |
+
<ArrowLeftRight size={11} style={{ color: "oklch(0.72 0.17 160)" }} />
|
| 81 |
+
<span className="text-[10px] text-[oklch(0.60_0.010_264)]">
|
| 82 |
+
Formatted SQL differs from original
|
| 83 |
+
</span>
|
| 84 |
+
</div>
|
| 85 |
+
)}
|
| 86 |
+
|
| 87 |
+
{/* Formatted SQL output */}
|
| 88 |
+
<div className="flex-1 overflow-auto">
|
| 89 |
+
<pre
|
| 90 |
+
className="p-4 text-xs font-mono leading-relaxed text-[oklch(0.85_0.010_264)] whitespace-pre-wrap break-words"
|
| 91 |
+
style={{ fontFamily: "'JetBrains Mono', monospace" }}
|
| 92 |
+
>
|
| 93 |
+
{result.formatted}
|
| 94 |
+
</pre>
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
);
|
| 98 |
+
}
|
frontend/src/components/InjectionPanel.tsx
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Shield, ShieldAlert, ShieldCheck, ShieldX, Info } from "lucide-react";
|
| 2 |
+
import type { InjectionResult, InjectionPattern } from "@/lib/api";
|
| 3 |
+
|
| 4 |
+
interface InjectionPanelProps {
|
| 5 |
+
result: InjectionResult | null;
|
| 6 |
+
onJumpToLine?: (line: number) => void;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
const RISK_CONFIG = {
|
| 10 |
+
critical: {
|
| 11 |
+
label: "CRITICAL",
|
| 12 |
+
color: "oklch(0.60 0.20 25)",
|
| 13 |
+
bg: "oklch(0.60 0.20 25 / 0.12)",
|
| 14 |
+
border: "oklch(0.60 0.20 25 / 0.35)",
|
| 15 |
+
icon: ShieldX,
|
| 16 |
+
},
|
| 17 |
+
high: {
|
| 18 |
+
label: "HIGH",
|
| 19 |
+
color: "oklch(0.72 0.18 55)",
|
| 20 |
+
bg: "oklch(0.72 0.18 55 / 0.12)",
|
| 21 |
+
border: "oklch(0.72 0.18 55 / 0.35)",
|
| 22 |
+
icon: ShieldAlert,
|
| 23 |
+
},
|
| 24 |
+
medium: {
|
| 25 |
+
label: "MEDIUM",
|
| 26 |
+
color: "oklch(0.78 0.18 90)",
|
| 27 |
+
bg: "oklch(0.78 0.18 90 / 0.12)",
|
| 28 |
+
border: "oklch(0.78 0.18 90 / 0.35)",
|
| 29 |
+
icon: ShieldAlert,
|
| 30 |
+
},
|
| 31 |
+
low: {
|
| 32 |
+
label: "LOW",
|
| 33 |
+
color: "oklch(0.68 0.16 210)",
|
| 34 |
+
bg: "oklch(0.68 0.16 210 / 0.12)",
|
| 35 |
+
border: "oklch(0.68 0.16 210 / 0.35)",
|
| 36 |
+
icon: Shield,
|
| 37 |
+
},
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
function RiskMeter({ score }: { score: number }) {
|
| 41 |
+
const color =
|
| 42 |
+
score >= 75 ? "oklch(0.60 0.20 25)" :
|
| 43 |
+
score >= 50 ? "oklch(0.72 0.18 55)" :
|
| 44 |
+
score >= 25 ? "oklch(0.78 0.18 90)" :
|
| 45 |
+
"oklch(0.72 0.17 160)";
|
| 46 |
+
|
| 47 |
+
return (
|
| 48 |
+
<div className="flex items-center gap-3">
|
| 49 |
+
<div className="flex-1 h-1.5 rounded-full bg-[oklch(0.20_0.010_264)] overflow-hidden">
|
| 50 |
+
<div
|
| 51 |
+
className="h-full rounded-full transition-all duration-500"
|
| 52 |
+
style={{ width: `${score}%`, background: color }}
|
| 53 |
+
/>
|
| 54 |
+
</div>
|
| 55 |
+
<span className="text-sm font-mono font-semibold w-12 text-right" style={{ color }}>
|
| 56 |
+
{score}/100
|
| 57 |
+
</span>
|
| 58 |
+
</div>
|
| 59 |
+
);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
function PatternCard({ pattern, onJumpToLine }: { pattern: InjectionPattern; onJumpToLine?: (line: number) => void }) {
|
| 63 |
+
const cfg = RISK_CONFIG[pattern.risk_level] ?? RISK_CONFIG.low;
|
| 64 |
+
const Icon = cfg.icon;
|
| 65 |
+
|
| 66 |
+
return (
|
| 67 |
+
<div
|
| 68 |
+
className="mx-3 mb-2 rounded-lg border overflow-hidden"
|
| 69 |
+
style={{ borderColor: cfg.border, background: cfg.bg }}
|
| 70 |
+
>
|
| 71 |
+
{/* Header */}
|
| 72 |
+
<div className="flex items-center gap-2 px-3 py-2 border-b" style={{ borderColor: cfg.border }}>
|
| 73 |
+
<Icon size={14} style={{ color: cfg.color }} className="flex-shrink-0" />
|
| 74 |
+
<span className="text-xs font-semibold flex-1" style={{ color: cfg.color }}>
|
| 75 |
+
{pattern.description}
|
| 76 |
+
</span>
|
| 77 |
+
<span
|
| 78 |
+
className="text-[10px] font-bold px-1.5 py-0.5 rounded"
|
| 79 |
+
style={{ background: cfg.bg, color: cfg.color, border: `1px solid ${cfg.border}` }}
|
| 80 |
+
>
|
| 81 |
+
{cfg.label}
|
| 82 |
+
</span>
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
{/* Body */}
|
| 86 |
+
<div className="px-3 py-2 space-y-2">
|
| 87 |
+
{/* Category */}
|
| 88 |
+
<div className="flex items-center gap-2">
|
| 89 |
+
<span className="text-[10px] text-[oklch(0.45_0.010_264)]">Category:</span>
|
| 90 |
+
<span className="text-[10px] font-mono font-medium" style={{ color: cfg.color }}>
|
| 91 |
+
{pattern.category}
|
| 92 |
+
</span>
|
| 93 |
+
{pattern.line_no && (
|
| 94 |
+
<button
|
| 95 |
+
className="text-[10px] font-mono ml-auto text-[oklch(0.45_0.010_264)] hover:text-[oklch(0.68_0.16_210)] transition-colors"
|
| 96 |
+
onClick={() => pattern.line_no && onJumpToLine?.(pattern.line_no)}
|
| 97 |
+
>
|
| 98 |
+
L{pattern.line_no}:{pattern.line_pos}
|
| 99 |
+
</button>
|
| 100 |
+
)}
|
| 101 |
+
</div>
|
| 102 |
+
|
| 103 |
+
{/* Detail */}
|
| 104 |
+
<p className="text-xs text-[oklch(0.72_0.010_264)] leading-relaxed">{pattern.detail}</p>
|
| 105 |
+
|
| 106 |
+
{/* Offending token */}
|
| 107 |
+
{pattern.offending_token && (
|
| 108 |
+
<div className="rounded px-2 py-1.5" style={{ background: "oklch(0.10 0.008 264)" }}>
|
| 109 |
+
<p className="text-[10px] text-[oklch(0.45_0.010_264)] mb-1">Offending token:</p>
|
| 110 |
+
<code className="text-xs font-mono break-all" style={{ color: cfg.color }}>
|
| 111 |
+
{pattern.offending_token}
|
| 112 |
+
</code>
|
| 113 |
+
</div>
|
| 114 |
+
)}
|
| 115 |
+
|
| 116 |
+
{/* Recommendation */}
|
| 117 |
+
<div className="flex items-start gap-1.5 pt-1">
|
| 118 |
+
<Info size={10} className="flex-shrink-0 mt-0.5 text-[oklch(0.45_0.010_264)]" />
|
| 119 |
+
<p className="text-[10px] text-[oklch(0.55_0.010_264)] leading-relaxed">{pattern.recommendation}</p>
|
| 120 |
+
</div>
|
| 121 |
+
</div>
|
| 122 |
+
</div>
|
| 123 |
+
);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
export function InjectionPanel({ result, onJumpToLine }: InjectionPanelProps) {
|
| 127 |
+
if (!result) {
|
| 128 |
+
return (
|
| 129 |
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-[oklch(0.45_0.010_264)]">
|
| 130 |
+
<Shield size={32} className="opacity-30" />
|
| 131 |
+
<p className="text-sm">Run analysis to check for injection risks</p>
|
| 132 |
+
</div>
|
| 133 |
+
);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
const { safe, risk_score, patterns, summary } = result;
|
| 137 |
+
|
| 138 |
+
const summaryColor =
|
| 139 |
+
risk_score >= 75 ? "oklch(0.60 0.20 25)" :
|
| 140 |
+
risk_score >= 50 ? "oklch(0.72 0.18 55)" :
|
| 141 |
+
risk_score >= 25 ? "oklch(0.78 0.18 90)" :
|
| 142 |
+
"oklch(0.72 0.17 160)";
|
| 143 |
+
|
| 144 |
+
const SummaryIcon = safe ? ShieldCheck : risk_score >= 75 ? ShieldX : ShieldAlert;
|
| 145 |
+
|
| 146 |
+
return (
|
| 147 |
+
<div className="flex flex-col h-full">
|
| 148 |
+
{/* Summary header */}
|
| 149 |
+
<div className="px-3 py-3 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0 space-y-2">
|
| 150 |
+
<div className="flex items-center gap-2">
|
| 151 |
+
<SummaryIcon size={16} style={{ color: summaryColor }} />
|
| 152 |
+
<span className="text-xs font-semibold" style={{ color: summaryColor }}>
|
| 153 |
+
{safe ? "No injection patterns detected" : `${patterns.length} pattern${patterns.length !== 1 ? "s" : ""} detected`}
|
| 154 |
+
</span>
|
| 155 |
+
</div>
|
| 156 |
+
<RiskMeter score={risk_score} />
|
| 157 |
+
<p className="text-xs text-[oklch(0.60_0.010_264)] leading-relaxed">{summary}</p>
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
{/* Pattern cards */}
|
| 161 |
+
{safe ? (
|
| 162 |
+
<div className="flex flex-col items-center justify-center flex-1 gap-3">
|
| 163 |
+
<ShieldCheck size={40} style={{ color: "oklch(0.72 0.17 160)" }} className="opacity-60" />
|
| 164 |
+
<p className="text-sm font-medium" style={{ color: "oklch(0.72 0.17 160)" }}>SQL appears safe</p>
|
| 165 |
+
<p className="text-xs text-[oklch(0.45_0.010_264)]">No known injection patterns were found</p>
|
| 166 |
+
</div>
|
| 167 |
+
) : (
|
| 168 |
+
<div className="flex-1 overflow-auto pt-2">
|
| 169 |
+
{patterns.map((p) => (
|
| 170 |
+
<PatternCard key={p.pattern_id} pattern={p} onJumpToLine={onJumpToLine} />
|
| 171 |
+
))}
|
| 172 |
+
</div>
|
| 173 |
+
)}
|
| 174 |
+
</div>
|
| 175 |
+
);
|
| 176 |
+
}
|
frontend/src/components/LintPanel.tsx
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { AlertCircle, AlertTriangle, CheckCircle2, Wrench } from "lucide-react";
|
| 2 |
+
import type { LintResult, LintViolation } from "@/lib/api";
|
| 3 |
+
|
| 4 |
+
interface LintPanelProps {
|
| 5 |
+
result: LintResult | null;
|
| 6 |
+
onJumpToLine?: (line: number) => void;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
function SeverityBadge({ warning }: { warning: boolean }) {
|
| 10 |
+
if (warning) {
|
| 11 |
+
return (
|
| 12 |
+
<span className="inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded"
|
| 13 |
+
style={{ background: "oklch(0.78 0.18 55 / 0.15)", color: "oklch(0.78 0.18 55)", border: "1px solid oklch(0.78 0.18 55 / 0.3)" }}>
|
| 14 |
+
<AlertTriangle size={9} />
|
| 15 |
+
WARN
|
| 16 |
+
</span>
|
| 17 |
+
);
|
| 18 |
+
}
|
| 19 |
+
return (
|
| 20 |
+
<span className="inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded"
|
| 21 |
+
style={{ background: "oklch(0.60 0.20 25 / 0.15)", color: "oklch(0.65 0.20 25)", border: "1px solid oklch(0.60 0.20 25 / 0.3)" }}>
|
| 22 |
+
<AlertCircle size={9} />
|
| 23 |
+
ERROR
|
| 24 |
+
</span>
|
| 25 |
+
);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
function RuleCodeBadge({ code }: { code: string }) {
|
| 29 |
+
return (
|
| 30 |
+
<span className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded"
|
| 31 |
+
style={{ background: "oklch(0.68 0.16 210 / 0.12)", color: "oklch(0.68 0.16 210)", border: "1px solid oklch(0.68 0.16 210 / 0.25)" }}>
|
| 32 |
+
{code}
|
| 33 |
+
</span>
|
| 34 |
+
);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
function ViolationRow({ v, onJumpToLine }: { v: LintViolation; onJumpToLine?: (line: number) => void }) {
|
| 38 |
+
return (
|
| 39 |
+
<div
|
| 40 |
+
className="group flex flex-col gap-1.5 px-3 py-2.5 border-b border-[oklch(0.18_0.010_264)] hover:bg-[oklch(0.16_0.010_264)] cursor-pointer transition-colors"
|
| 41 |
+
onClick={() => onJumpToLine?.(v.line_no)}
|
| 42 |
+
>
|
| 43 |
+
<div className="flex items-center gap-2 flex-wrap">
|
| 44 |
+
<SeverityBadge warning={v.warning} />
|
| 45 |
+
<RuleCodeBadge code={v.code} />
|
| 46 |
+
<span className="text-[10px] font-mono text-[oklch(0.45_0.010_264)] ml-auto group-hover:text-[oklch(0.55_0.010_264)] transition-colors">
|
| 47 |
+
L{v.line_no}:{v.line_pos}
|
| 48 |
+
</span>
|
| 49 |
+
{v.fixable && (
|
| 50 |
+
<span className="inline-flex items-center gap-0.5 text-[10px]"
|
| 51 |
+
style={{ color: "oklch(0.72 0.17 160)" }}>
|
| 52 |
+
<Wrench size={9} />
|
| 53 |
+
fixable
|
| 54 |
+
</span>
|
| 55 |
+
)}
|
| 56 |
+
</div>
|
| 57 |
+
<p className="text-xs text-[oklch(0.78_0.010_264)] leading-relaxed">{v.description}</p>
|
| 58 |
+
</div>
|
| 59 |
+
);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
export function LintPanel({ result, onJumpToLine }: LintPanelProps) {
|
| 63 |
+
if (!result) {
|
| 64 |
+
return (
|
| 65 |
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-[oklch(0.45_0.010_264)]">
|
| 66 |
+
<AlertCircle size={32} className="opacity-30" />
|
| 67 |
+
<p className="text-sm">Run analysis to see lint results</p>
|
| 68 |
+
</div>
|
| 69 |
+
);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
const { violations, passed, stats } = result;
|
| 73 |
+
|
| 74 |
+
return (
|
| 75 |
+
<div className="flex flex-col h-full">
|
| 76 |
+
{/* Summary bar */}
|
| 77 |
+
<div className="flex items-center gap-4 px-3 py-2 border-b border-[oklch(0.22_0.010_264)] flex-shrink-0">
|
| 78 |
+
{passed ? (
|
| 79 |
+
<div className="flex items-center gap-1.5 text-xs" style={{ color: "oklch(0.72 0.17 160)" }}>
|
| 80 |
+
<CheckCircle2 size={14} />
|
| 81 |
+
<span className="font-medium">All checks passed</span>
|
| 82 |
+
</div>
|
| 83 |
+
) : (
|
| 84 |
+
<div className="flex items-center gap-1.5 text-xs" style={{ color: "oklch(0.65 0.20 25)" }}>
|
| 85 |
+
<AlertCircle size={14} />
|
| 86 |
+
<span className="font-medium">{stats.total} violation{stats.total !== 1 ? "s" : ""}</span>
|
| 87 |
+
</div>
|
| 88 |
+
)}
|
| 89 |
+
<div className="flex items-center gap-3 ml-auto text-[10px] text-[oklch(0.45_0.010_264)]">
|
| 90 |
+
{stats.errors > 0 && (
|
| 91 |
+
<span style={{ color: "oklch(0.65 0.20 25)" }}>{stats.errors} error{stats.errors !== 1 ? "s" : ""}</span>
|
| 92 |
+
)}
|
| 93 |
+
{stats.warnings > 0 && (
|
| 94 |
+
<span style={{ color: "oklch(0.78 0.18 55)" }}>{stats.warnings} warning{stats.warnings !== 1 ? "s" : ""}</span>
|
| 95 |
+
)}
|
| 96 |
+
{stats.fixable > 0 && (
|
| 97 |
+
<span style={{ color: "oklch(0.72 0.17 160)" }}>{stats.fixable} fixable</span>
|
| 98 |
+
)}
|
| 99 |
+
</div>
|
| 100 |
+
</div>
|
| 101 |
+
|
| 102 |
+
{/* Violations list */}
|
| 103 |
+
{passed ? (
|
| 104 |
+
<div className="flex flex-col items-center justify-center h-full gap-3">
|
| 105 |
+
<CheckCircle2 size={40} style={{ color: "oklch(0.72 0.17 160)" }} className="opacity-60" />
|
| 106 |
+
<p className="text-sm font-medium" style={{ color: "oklch(0.72 0.17 160)" }}>No violations found</p>
|
| 107 |
+
<p className="text-xs text-[oklch(0.45_0.010_264)]">Your SQL is clean for dialect: {result.dialect}</p>
|
| 108 |
+
</div>
|
| 109 |
+
) : (
|
| 110 |
+
<div className="flex-1 overflow-auto">
|
| 111 |
+
{violations.map((v, i) => (
|
| 112 |
+
<ViolationRow key={`${v.code}-${v.line_no}-${v.line_pos}-${i}`} v={v} onJumpToLine={onJumpToLine} />
|
| 113 |
+
))}
|
| 114 |
+
</div>
|
| 115 |
+
)}
|
| 116 |
+
</div>
|
| 117 |
+
);
|
| 118 |
+
}
|
frontend/src/components/SqlEditor.tsx
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useRef, useCallback } from "react";
|
| 2 |
+
import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightActiveLine, drawSelection, dropCursor } from "@codemirror/view";
|
| 3 |
+
import { EditorState, Compartment } from "@codemirror/state";
|
| 4 |
+
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
| 5 |
+
import { sql, MySQL, PostgreSQL, StandardSQL, MSSQL, SQLite } from "@codemirror/lang-sql";
|
| 6 |
+
import { oneDark } from "@codemirror/theme-one-dark";
|
| 7 |
+
import { bracketMatching, indentOnInput, syntaxHighlighting, defaultHighlightStyle, foldGutter, foldKeymap } from "@codemirror/language";
|
| 8 |
+
import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
|
| 9 |
+
import { lintKeymap } from "@codemirror/lint";
|
| 10 |
+
|
| 11 |
+
interface SqlEditorProps {
|
| 12 |
+
value: string;
|
| 13 |
+
onChange: (value: string) => void;
|
| 14 |
+
onAnalyze: () => void;
|
| 15 |
+
dialect: string;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const dialectMap: Record<string, unknown> = {
|
| 19 |
+
mysql: MySQL,
|
| 20 |
+
postgres: PostgreSQL,
|
| 21 |
+
tsql: MSSQL,
|
| 22 |
+
sqlite: SQLite,
|
| 23 |
+
ansi: StandardSQL,
|
| 24 |
+
};
|
| 25 |
+
|
| 26 |
+
const dialectCompartment = new Compartment();
|
| 27 |
+
|
| 28 |
+
function getDialectExtension(dialect: string) {
|
| 29 |
+
const schema = dialectMap[dialect] ?? StandardSQL;
|
| 30 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 31 |
+
return sql({ dialect: schema as any });
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export function SqlEditor({ value, onChange, onAnalyze, dialect }: SqlEditorProps) {
|
| 35 |
+
const containerRef = useRef<HTMLDivElement>(null);
|
| 36 |
+
const viewRef = useRef<EditorView | null>(null);
|
| 37 |
+
|
| 38 |
+
const onAnalyzeRef = useRef(onAnalyze);
|
| 39 |
+
onAnalyzeRef.current = onAnalyze;
|
| 40 |
+
|
| 41 |
+
// Create editor once
|
| 42 |
+
useEffect(() => {
|
| 43 |
+
if (!containerRef.current) return;
|
| 44 |
+
|
| 45 |
+
const analyzeKeymap = keymap.of([
|
| 46 |
+
{
|
| 47 |
+
key: "Ctrl-Enter",
|
| 48 |
+
mac: "Cmd-Enter",
|
| 49 |
+
run: () => {
|
| 50 |
+
onAnalyzeRef.current();
|
| 51 |
+
return true;
|
| 52 |
+
},
|
| 53 |
+
},
|
| 54 |
+
]);
|
| 55 |
+
|
| 56 |
+
const state = EditorState.create({
|
| 57 |
+
doc: value,
|
| 58 |
+
extensions: [
|
| 59 |
+
lineNumbers(),
|
| 60 |
+
highlightActiveLineGutter(),
|
| 61 |
+
highlightActiveLine(),
|
| 62 |
+
history(),
|
| 63 |
+
drawSelection(),
|
| 64 |
+
dropCursor(),
|
| 65 |
+
indentOnInput(),
|
| 66 |
+
bracketMatching(),
|
| 67 |
+
closeBrackets(),
|
| 68 |
+
autocompletion(),
|
| 69 |
+
foldGutter(),
|
| 70 |
+
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
| 71 |
+
dialectCompartment.of(getDialectExtension(dialect)),
|
| 72 |
+
oneDark,
|
| 73 |
+
keymap.of([
|
| 74 |
+
...closeBracketsKeymap,
|
| 75 |
+
...defaultKeymap,
|
| 76 |
+
...historyKeymap,
|
| 77 |
+
...foldKeymap,
|
| 78 |
+
...completionKeymap,
|
| 79 |
+
...lintKeymap,
|
| 80 |
+
indentWithTab,
|
| 81 |
+
]),
|
| 82 |
+
analyzeKeymap,
|
| 83 |
+
EditorView.updateListener.of((update) => {
|
| 84 |
+
if (update.docChanged) {
|
| 85 |
+
onChange(update.state.doc.toString());
|
| 86 |
+
}
|
| 87 |
+
}),
|
| 88 |
+
EditorView.theme({
|
| 89 |
+
"&": {
|
| 90 |
+
height: "100%",
|
| 91 |
+
backgroundColor: "oklch(0.10 0.008 264)",
|
| 92 |
+
},
|
| 93 |
+
".cm-content": {
|
| 94 |
+
padding: "12px 0",
|
| 95 |
+
caretColor: "oklch(0.68 0.16 210)",
|
| 96 |
+
},
|
| 97 |
+
".cm-line": {
|
| 98 |
+
padding: "0 16px 0 8px",
|
| 99 |
+
},
|
| 100 |
+
".cm-cursor": {
|
| 101 |
+
borderLeftColor: "oklch(0.68 0.16 210)",
|
| 102 |
+
borderLeftWidth: "2px",
|
| 103 |
+
},
|
| 104 |
+
}),
|
| 105 |
+
],
|
| 106 |
+
});
|
| 107 |
+
|
| 108 |
+
const view = new EditorView({ state, parent: containerRef.current });
|
| 109 |
+
viewRef.current = view;
|
| 110 |
+
|
| 111 |
+
return () => {
|
| 112 |
+
view.destroy();
|
| 113 |
+
viewRef.current = null;
|
| 114 |
+
};
|
| 115 |
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
| 116 |
+
}, []);
|
| 117 |
+
|
| 118 |
+
// Sync dialect changes
|
| 119 |
+
useEffect(() => {
|
| 120 |
+
const view = viewRef.current;
|
| 121 |
+
if (!view) return;
|
| 122 |
+
view.dispatch({
|
| 123 |
+
effects: dialectCompartment.reconfigure(getDialectExtension(dialect)),
|
| 124 |
+
});
|
| 125 |
+
}, [dialect]);
|
| 126 |
+
|
| 127 |
+
// Sync external value changes (e.g. "apply formatted")
|
| 128 |
+
const lastValueRef = useRef(value);
|
| 129 |
+
useEffect(() => {
|
| 130 |
+
const view = viewRef.current;
|
| 131 |
+
if (!view) return;
|
| 132 |
+
const current = view.state.doc.toString();
|
| 133 |
+
if (current !== value && value !== lastValueRef.current) {
|
| 134 |
+
view.dispatch({
|
| 135 |
+
changes: { from: 0, to: current.length, insert: value },
|
| 136 |
+
});
|
| 137 |
+
}
|
| 138 |
+
lastValueRef.current = value;
|
| 139 |
+
}, [value]);
|
| 140 |
+
|
| 141 |
+
// Jump to line
|
| 142 |
+
const jumpToLine = useCallback((lineNo: number) => {
|
| 143 |
+
const view = viewRef.current;
|
| 144 |
+
if (!view) return;
|
| 145 |
+
const line = view.state.doc.line(Math.max(1, Math.min(lineNo, view.state.doc.lines)));
|
| 146 |
+
view.dispatch({
|
| 147 |
+
selection: { anchor: line.from },
|
| 148 |
+
scrollIntoView: true,
|
| 149 |
+
});
|
| 150 |
+
view.focus();
|
| 151 |
+
}, []);
|
| 152 |
+
|
| 153 |
+
// Expose jumpToLine via a custom event
|
| 154 |
+
useEffect(() => {
|
| 155 |
+
const el = containerRef.current;
|
| 156 |
+
if (!el) return;
|
| 157 |
+
const handler = (e: Event) => {
|
| 158 |
+
const lineNo = (e as CustomEvent<number>).detail;
|
| 159 |
+
jumpToLine(lineNo);
|
| 160 |
+
};
|
| 161 |
+
el.addEventListener("jump-to-line", handler);
|
| 162 |
+
return () => el.removeEventListener("jump-to-line", handler);
|
| 163 |
+
}, [jumpToLine]);
|
| 164 |
+
|
| 165 |
+
return (
|
| 166 |
+
<div
|
| 167 |
+
ref={containerRef}
|
| 168 |
+
className="h-full w-full overflow-hidden"
|
| 169 |
+
style={{ fontFamily: "'JetBrains Mono', monospace" }}
|
| 170 |
+
data-editor-container
|
| 171 |
+
/>
|
| 172 |
+
);
|
| 173 |
+
}
|
frontend/src/index.css
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import "tailwindcss";
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
--bg-base: #0d1117;
|
| 5 |
+
--bg-surface: #161b22;
|
| 6 |
+
--bg-elevated: #1c2128;
|
| 7 |
+
--bg-overlay: #21262d;
|
| 8 |
+
--border: #30363d;
|
| 9 |
+
--border-muted: #21262d;
|
| 10 |
+
|
| 11 |
+
--text-primary: #e6edf3;
|
| 12 |
+
--text-secondary: #8b949e;
|
| 13 |
+
--text-muted: #484f58;
|
| 14 |
+
|
| 15 |
+
--accent-blue: #58a6ff;
|
| 16 |
+
--accent-cyan: #39d353;
|
| 17 |
+
--accent-purple: #bc8cff;
|
| 18 |
+
--accent-orange: #f78166;
|
| 19 |
+
--accent-yellow: #e3b341;
|
| 20 |
+
--accent-red: #ff7b72;
|
| 21 |
+
--accent-green: #3fb950;
|
| 22 |
+
|
| 23 |
+
--font-sans: "Inter", system-ui, sans-serif;
|
| 24 |
+
--font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
*, *::before, *::after { box-sizing: border-box; }
|
| 28 |
+
|
| 29 |
+
html, body, #root {
|
| 30 |
+
height: 100%;
|
| 31 |
+
margin: 0;
|
| 32 |
+
padding: 0;
|
| 33 |
+
background: var(--bg-base);
|
| 34 |
+
color: var(--text-primary);
|
| 35 |
+
font-family: var(--font-sans);
|
| 36 |
+
font-size: 14px;
|
| 37 |
+
line-height: 1.5;
|
| 38 |
+
-webkit-font-smoothing: antialiased;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/* Scrollbars */
|
| 42 |
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
| 43 |
+
::-webkit-scrollbar-track { background: transparent; }
|
| 44 |
+
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
| 45 |
+
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
| 46 |
+
|
| 47 |
+
/* CodeMirror dark overrides */
|
| 48 |
+
.cm-editor {
|
| 49 |
+
height: 100%;
|
| 50 |
+
font-family: var(--font-mono) !important;
|
| 51 |
+
font-size: 13px !important;
|
| 52 |
+
background: var(--bg-base) !important;
|
| 53 |
+
}
|
| 54 |
+
.cm-editor.cm-focused { outline: none !important; }
|
| 55 |
+
.cm-scroller { overflow: auto !important; }
|
| 56 |
+
.cm-gutters {
|
| 57 |
+
background: var(--bg-surface) !important;
|
| 58 |
+
border-right: 1px solid var(--border) !important;
|
| 59 |
+
color: var(--text-muted) !important;
|
| 60 |
+
}
|
| 61 |
+
.cm-activeLineGutter { background: var(--bg-elevated) !important; }
|
| 62 |
+
.cm-activeLine { background: rgba(88, 166, 255, 0.04) !important; }
|
| 63 |
+
.cm-selectionBackground { background: rgba(88, 166, 255, 0.15) !important; }
|
| 64 |
+
.cm-cursor { border-left-color: var(--accent-blue) !important; }
|
| 65 |
+
.cm-content { caret-color: var(--accent-blue) !important; }
|
| 66 |
+
|
| 67 |
+
/* AST tree */
|
| 68 |
+
.ast-tree { font-family: var(--font-mono); font-size: 12px; }
|
| 69 |
+
.ast-node-toggle { cursor: pointer; user-select: none; }
|
| 70 |
+
.ast-node-toggle:hover { opacity: 0.8; }
|
| 71 |
+
|
| 72 |
+
/* Resizable panel handle */
|
| 73 |
+
[data-panel-resize-handle-id] {
|
| 74 |
+
background: var(--border);
|
| 75 |
+
transition: background 0.15s;
|
| 76 |
+
}
|
| 77 |
+
[data-panel-resize-handle-id]:hover,
|
| 78 |
+
[data-panel-resize-handle-id][data-resize-handle-active] {
|
| 79 |
+
background: var(--accent-blue);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/* Swagger UI embed */
|
| 83 |
+
.swagger-ui-frame {
|
| 84 |
+
width: 100%;
|
| 85 |
+
height: 100%;
|
| 86 |
+
border: none;
|
| 87 |
+
background: #fff;
|
| 88 |
+
}
|
frontend/src/lib/api.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* SQL Analyzer REST API client
|
| 3 |
+
* Plain fetch() — no tRPC, no Node.js dependency.
|
| 4 |
+
* Base URL is determined by the VITE_API_BASE env var (defaults to "").
|
| 5 |
+
* In production the React bundle is served by FastAPI itself, so "" works.
|
| 6 |
+
* In dev, Vite proxies /api/* to http://localhost:7860.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
const BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? "";
|
| 10 |
+
|
| 11 |
+
async function post<T>(path: string, body: unknown): Promise<T> {
|
| 12 |
+
const res = await fetch(`${BASE}${path}`, {
|
| 13 |
+
method: "POST",
|
| 14 |
+
headers: { "Content-Type": "application/json" },
|
| 15 |
+
body: JSON.stringify(body),
|
| 16 |
+
});
|
| 17 |
+
if (!res.ok) {
|
| 18 |
+
const text = await res.text().catch(() => "");
|
| 19 |
+
throw new Error(`API error ${res.status}: ${text.slice(0, 400)}`);
|
| 20 |
+
}
|
| 21 |
+
return res.json() as Promise<T>;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
async function get<T>(path: string): Promise<T> {
|
| 25 |
+
const res = await fetch(`${BASE}${path}`);
|
| 26 |
+
if (!res.ok) {
|
| 27 |
+
const text = await res.text().catch(() => "");
|
| 28 |
+
throw new Error(`API error ${res.status}: ${text.slice(0, 400)}`);
|
| 29 |
+
}
|
| 30 |
+
return res.json() as Promise<T>;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
// ── Types ──────────────────────────────────────────────────────────────────
|
| 34 |
+
|
| 35 |
+
export interface LintViolation {
|
| 36 |
+
line_no: number;
|
| 37 |
+
line_pos: number;
|
| 38 |
+
code: string;
|
| 39 |
+
description: string;
|
| 40 |
+
name: string;
|
| 41 |
+
warning: boolean;
|
| 42 |
+
fixable: boolean;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
export interface LintResult {
|
| 46 |
+
dialect: string;
|
| 47 |
+
violations: LintViolation[];
|
| 48 |
+
passed: boolean;
|
| 49 |
+
stats: { total: number; errors: number; warnings: number; fixable: number };
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
export interface AstNode {
|
| 53 |
+
id: string;
|
| 54 |
+
type: string;
|
| 55 |
+
name: string;
|
| 56 |
+
raw: string | null;
|
| 57 |
+
start_line: number | null;
|
| 58 |
+
start_pos: number | null;
|
| 59 |
+
end_line: number | null;
|
| 60 |
+
end_pos: number | null;
|
| 61 |
+
is_leaf: boolean;
|
| 62 |
+
children: AstNode[];
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
export interface ParseResult {
|
| 66 |
+
dialect: string;
|
| 67 |
+
tree: AstNode;
|
| 68 |
+
token_count: number;
|
| 69 |
+
depth: number;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
export interface FormatResult {
|
| 73 |
+
dialect: string;
|
| 74 |
+
original: string;
|
| 75 |
+
formatted: string;
|
| 76 |
+
changed: boolean;
|
| 77 |
+
fixes_applied: number;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
export interface InjectionPattern {
|
| 81 |
+
pattern_id: string;
|
| 82 |
+
risk_level: "critical" | "high" | "medium" | "low";
|
| 83 |
+
category: string;
|
| 84 |
+
description: string;
|
| 85 |
+
detail: string;
|
| 86 |
+
offending_token: string | null;
|
| 87 |
+
line_no: number | null;
|
| 88 |
+
line_pos: number | null;
|
| 89 |
+
recommendation: string;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
export interface InjectionResult {
|
| 93 |
+
dialect: string;
|
| 94 |
+
safe: boolean;
|
| 95 |
+
risk_score: number;
|
| 96 |
+
patterns: InjectionPattern[];
|
| 97 |
+
summary: string;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
export interface HealthResult {
|
| 101 |
+
status: string;
|
| 102 |
+
version: string;
|
| 103 |
+
dialects: string[];
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// ── API calls ──────────────────────────────────────────────────────────────
|
| 107 |
+
|
| 108 |
+
export const api = {
|
| 109 |
+
health: () => get<HealthResult>("/api/health"),
|
| 110 |
+
lint: (sql: string, dialect: string) => post<LintResult>("/api/lint", { sql, dialect }),
|
| 111 |
+
parse: (sql: string, dialect: string) => post<ParseResult>("/api/parse", { sql, dialect }),
|
| 112 |
+
format: (sql: string, dialect: string) => post<FormatResult>("/api/format", { sql, dialect }),
|
| 113 |
+
inject: (sql: string, dialect: string) => post<InjectionResult>("/api/inject", { sql, dialect }),
|
| 114 |
+
};
|
frontend/src/main.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StrictMode } from "react";
|
| 2 |
+
import { createRoot } from "react-dom/client";
|
| 3 |
+
import "./index.css";
|
| 4 |
+
import App from "./App";
|
| 5 |
+
|
| 6 |
+
createRoot(document.getElementById("root")!).render(
|
| 7 |
+
<StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</StrictMode>
|
| 10 |
+
);
|
frontend/src/pages/Analyzer.tsx
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useRef, useCallback, useEffect } from "react";
|
| 2 |
+
import { PanelGroup, Panel, PanelResizeHandle } from "react-resizable-panels";
|
| 3 |
+
import { Play, Loader2, BookOpen, Zap, Database } from "lucide-react";
|
| 4 |
+
import { toast } from "sonner";
|
| 5 |
+
import { Link } from "wouter";
|
| 6 |
+
import { api, type LintResult, type ParseResult, type FormatResult, type InjectionResult, type HealthResult } from "@/lib/api";
|
| 7 |
+
import { SqlEditor } from "@/components/SqlEditor";
|
| 8 |
+
import { AstTreeView } from "@/components/AstTreeView";
|
| 9 |
+
import { LintPanel } from "@/components/LintPanel";
|
| 10 |
+
import { InjectionPanel } from "@/components/InjectionPanel";
|
| 11 |
+
import { FormatterPanel } from "@/components/FormatterPanel";
|
| 12 |
+
|
| 13 |
+
// ── Constants ──────────────────────────────────────────────────────────────
|
| 14 |
+
|
| 15 |
+
type AnalysisTab = "ast" | "lint" | "injection" | "formatted";
|
| 16 |
+
|
| 17 |
+
const TABS: { id: AnalysisTab; label: string }[] = [
|
| 18 |
+
{ id: "ast", label: "AST Tree" },
|
| 19 |
+
{ id: "lint", label: "Lint" },
|
| 20 |
+
{ id: "injection", label: "Injection" },
|
| 21 |
+
{ id: "formatted", label: "Formatted" },
|
| 22 |
+
];
|
| 23 |
+
|
| 24 |
+
const DIALECTS = [
|
| 25 |
+
{ value: "ansi", label: "ANSI SQL" },
|
| 26 |
+
{ value: "postgres", label: "PostgreSQL" },
|
| 27 |
+
{ value: "mysql", label: "MySQL" },
|
| 28 |
+
{ value: "tsql", label: "T-SQL" },
|
| 29 |
+
{ value: "sqlite", label: "SQLite" },
|
| 30 |
+
{ value: "bigquery", label: "BigQuery" },
|
| 31 |
+
{ value: "snowflake", label: "Snowflake" },
|
| 32 |
+
{ value: "redshift", label: "Redshift" },
|
| 33 |
+
{ value: "duckdb", label: "DuckDB" },
|
| 34 |
+
{ value: "hive", label: "Hive" },
|
| 35 |
+
{ value: "sparksql", label: "Spark SQL" },
|
| 36 |
+
{ value: "trino", label: "Trino" },
|
| 37 |
+
{ value: "databricks", label: "Databricks" },
|
| 38 |
+
{ value: "oracle", label: "Oracle" },
|
| 39 |
+
{ value: "teradata", label: "Teradata" },
|
| 40 |
+
{ value: "clickhouse", label: "ClickHouse" },
|
| 41 |
+
{ value: "athena", label: "Athena" },
|
| 42 |
+
];
|
| 43 |
+
|
| 44 |
+
const DEFAULT_SQL = `-- SQL Analyzer Demo
|
| 45 |
+
-- Press Ctrl+Enter (or Cmd+Enter) to analyze
|
| 46 |
+
|
| 47 |
+
SELECT
|
| 48 |
+
u.id,
|
| 49 |
+
u.name,
|
| 50 |
+
u.email,
|
| 51 |
+
o.total,
|
| 52 |
+
o.created_at
|
| 53 |
+
FROM users u
|
| 54 |
+
INNER JOIN orders o ON u.id = o.user_id
|
| 55 |
+
WHERE u.active = 1
|
| 56 |
+
AND o.created_at >= '2024-01-01'
|
| 57 |
+
ORDER BY o.created_at DESC
|
| 58 |
+
LIMIT 50;`;
|
| 59 |
+
|
| 60 |
+
const INJECTION_SQL = `-- SQL Injection Demo
|
| 61 |
+
-- This query contains several injection patterns
|
| 62 |
+
|
| 63 |
+
SELECT * FROM users
|
| 64 |
+
WHERE username = 'admin' OR 1=1 --
|
| 65 |
+
AND password = 'anything';
|
| 66 |
+
|
| 67 |
+
-- Stacked query attempt
|
| 68 |
+
SELECT * FROM products WHERE id = 1;
|
| 69 |
+
DROP TABLE users;
|
| 70 |
+
|
| 71 |
+
-- UNION-based exfiltration
|
| 72 |
+
SELECT id, name FROM products
|
| 73 |
+
UNION SELECT username, password FROM users;`;
|
| 74 |
+
|
| 75 |
+
// ── Status indicator ───────────────────────────────────────────────────────
|
| 76 |
+
|
| 77 |
+
function StatusDot({ health }: { health: HealthResult | null }) {
|
| 78 |
+
const isOk = health?.status === "ok";
|
| 79 |
+
return (
|
| 80 |
+
<div className="flex items-center gap-1.5">
|
| 81 |
+
<div
|
| 82 |
+
className={`w-1.5 h-1.5 rounded-full ${isOk ? "animate-pulse" : ""}`}
|
| 83 |
+
style={{ background: isOk ? "var(--accent-green)" : "var(--accent-yellow)" }}
|
| 84 |
+
/>
|
| 85 |
+
<span className="text-[10px]" style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)" }}>
|
| 86 |
+
{isOk ? `SQLFluff ${health!.version}` : "starting…"}
|
| 87 |
+
</span>
|
| 88 |
+
</div>
|
| 89 |
+
);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
// ── Main page ──────────────────────────────────────────────────────────────
|
| 93 |
+
|
| 94 |
+
export default function Analyzer() {
|
| 95 |
+
const [sql, setSql] = useState(DEFAULT_SQL);
|
| 96 |
+
const [dialect, setDialect] = useState("ansi");
|
| 97 |
+
const [activeTab, setActiveTab] = useState<AnalysisTab>("ast");
|
| 98 |
+
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
| 99 |
+
|
| 100 |
+
const [health, setHealth] = useState<HealthResult | null>(null);
|
| 101 |
+
const [lintResult, setLintResult] = useState<LintResult | null>(null);
|
| 102 |
+
const [parseResult, setParseResult] = useState<ParseResult | null>(null);
|
| 103 |
+
const [formatResult, setFormatResult] = useState<FormatResult | null>(null);
|
| 104 |
+
const [injectResult, setInjectResult] = useState<InjectionResult | null>(null);
|
| 105 |
+
const [analysisTime, setAnalysisTime] = useState<number | null>(null);
|
| 106 |
+
|
| 107 |
+
const editorContainerRef = useRef<HTMLDivElement>(null);
|
| 108 |
+
|
| 109 |
+
// Poll health endpoint every 6 s until ready
|
| 110 |
+
useEffect(() => {
|
| 111 |
+
let cancelled = false;
|
| 112 |
+
async function poll() {
|
| 113 |
+
try {
|
| 114 |
+
const h = await api.health();
|
| 115 |
+
if (!cancelled) setHealth(h);
|
| 116 |
+
} catch {
|
| 117 |
+
// still starting
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
poll();
|
| 121 |
+
const id = setInterval(poll, 6000);
|
| 122 |
+
return () => { cancelled = true; clearInterval(id); };
|
| 123 |
+
}, []);
|
| 124 |
+
|
| 125 |
+
// ── Analyze ────────────────────────────────────────────────────────────
|
| 126 |
+
|
| 127 |
+
const handleAnalyze = useCallback(async () => {
|
| 128 |
+
if (!sql.trim()) { toast.error("Please enter some SQL to analyze"); return; }
|
| 129 |
+
if (health?.status !== "ok") { toast.error("API is not ready yet — please wait a moment"); return; }
|
| 130 |
+
|
| 131 |
+
setIsAnalyzing(true);
|
| 132 |
+
const start = Date.now();
|
| 133 |
+
|
| 134 |
+
try {
|
| 135 |
+
const [lint, parse, format, inject] = await Promise.allSettled([
|
| 136 |
+
api.lint(sql, dialect),
|
| 137 |
+
api.parse(sql, dialect),
|
| 138 |
+
api.format(sql, dialect),
|
| 139 |
+
api.inject(sql, dialect),
|
| 140 |
+
]);
|
| 141 |
+
|
| 142 |
+
if (lint.status === "fulfilled") setLintResult(lint.value);
|
| 143 |
+
if (parse.status === "fulfilled") setParseResult(parse.value);
|
| 144 |
+
if (format.status === "fulfilled") setFormatResult(format.value);
|
| 145 |
+
if (inject.status === "fulfilled") setInjectResult(inject.value);
|
| 146 |
+
|
| 147 |
+
setAnalysisTime(Date.now() - start);
|
| 148 |
+
|
| 149 |
+
const lv = lint.status === "fulfilled" ? lint.value : null;
|
| 150 |
+
const iv = inject.status === "fulfilled" ? inject.value : null;
|
| 151 |
+
if (lv && iv) {
|
| 152 |
+
if (!lv.passed || !iv.safe) {
|
| 153 |
+
toast.warning(
|
| 154 |
+
`Analysis complete — ${lv.stats.total} lint issue${lv.stats.total !== 1 ? "s" : ""}${!iv.safe ? `, risk score ${iv.risk_score}/100` : ""}`,
|
| 155 |
+
{ duration: 4000 }
|
| 156 |
+
);
|
| 157 |
+
} else {
|
| 158 |
+
toast.success("Analysis complete — no issues found", { duration: 3000 });
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
} catch (err) {
|
| 162 |
+
toast.error((err instanceof Error ? err.message : "Analysis failed").slice(0, 120));
|
| 163 |
+
} finally {
|
| 164 |
+
setIsAnalyzing(false);
|
| 165 |
+
}
|
| 166 |
+
}, [sql, dialect, health]);
|
| 167 |
+
|
| 168 |
+
// ── Jump to line ───────────────────────────────────────────────────────
|
| 169 |
+
|
| 170 |
+
const jumpToLine = useCallback((lineNo: number) => {
|
| 171 |
+
const container = editorContainerRef.current;
|
| 172 |
+
if (!container) return;
|
| 173 |
+
const editorEl = container.querySelector("[data-editor-container]");
|
| 174 |
+
if (!editorEl) return;
|
| 175 |
+
editorEl.dispatchEvent(new CustomEvent("jump-to-line", { detail: lineNo }));
|
| 176 |
+
}, []);
|
| 177 |
+
|
| 178 |
+
const lintBadge = lintResult && !lintResult.passed ? lintResult.stats.total : null;
|
| 179 |
+
const injectBadge = injectResult && !injectResult.safe ? injectResult.patterns.length : null;
|
| 180 |
+
|
| 181 |
+
// ── Render ─────────────────────────────────────────────────────────────
|
| 182 |
+
|
| 183 |
+
return (
|
| 184 |
+
<div className="flex flex-col h-screen" style={{ background: "var(--bg-base)", color: "var(--text-primary)" }}>
|
| 185 |
+
{/* Header */}
|
| 186 |
+
<header
|
| 187 |
+
className="flex items-center gap-3 px-4 py-2.5 flex-shrink-0"
|
| 188 |
+
style={{ borderBottom: "1px solid var(--border)", background: "var(--bg-surface)" }}
|
| 189 |
+
>
|
| 190 |
+
{/* Logo */}
|
| 191 |
+
<div className="flex items-center gap-2 mr-2">
|
| 192 |
+
<div
|
| 193 |
+
className="w-7 h-7 rounded flex items-center justify-center"
|
| 194 |
+
style={{ background: "rgba(88,166,255,0.12)", border: "1px solid rgba(88,166,255,0.25)" }}
|
| 195 |
+
>
|
| 196 |
+
<Database size={14} style={{ color: "var(--accent-blue)" }} />
|
| 197 |
+
</div>
|
| 198 |
+
<span className="text-sm font-semibold tracking-tight">SQL Analyzer</span>
|
| 199 |
+
</div>
|
| 200 |
+
|
| 201 |
+
{/* Dialect selector */}
|
| 202 |
+
<select
|
| 203 |
+
value={dialect}
|
| 204 |
+
onChange={(e) => setDialect(e.target.value)}
|
| 205 |
+
className="h-7 text-xs rounded px-2 pr-6 appearance-none cursor-pointer"
|
| 206 |
+
style={{
|
| 207 |
+
background: "var(--bg-elevated)",
|
| 208 |
+
border: "1px solid var(--border)",
|
| 209 |
+
color: "var(--text-primary)",
|
| 210 |
+
fontFamily: "var(--font-mono)",
|
| 211 |
+
outline: "none",
|
| 212 |
+
}}
|
| 213 |
+
>
|
| 214 |
+
{DIALECTS.map((d) => (
|
| 215 |
+
<option key={d.value} value={d.value}>{d.label}</option>
|
| 216 |
+
))}
|
| 217 |
+
</select>
|
| 218 |
+
|
| 219 |
+
{/* Example loader */}
|
| 220 |
+
<select
|
| 221 |
+
defaultValue=""
|
| 222 |
+
onChange={(e) => {
|
| 223 |
+
if (e.target.value === "demo") setSql(DEFAULT_SQL);
|
| 224 |
+
else if (e.target.value === "injection") setSql(INJECTION_SQL);
|
| 225 |
+
e.target.value = "";
|
| 226 |
+
}}
|
| 227 |
+
className="h-7 text-xs rounded px-2 pr-6 appearance-none cursor-pointer"
|
| 228 |
+
style={{
|
| 229 |
+
background: "var(--bg-elevated)",
|
| 230 |
+
border: "1px solid var(--border)",
|
| 231 |
+
color: "var(--text-secondary)",
|
| 232 |
+
outline: "none",
|
| 233 |
+
}}
|
| 234 |
+
>
|
| 235 |
+
<option value="" disabled>Examples…</option>
|
| 236 |
+
<option value="demo">Demo Query</option>
|
| 237 |
+
<option value="injection">Injection Demo</option>
|
| 238 |
+
</select>
|
| 239 |
+
|
| 240 |
+
<div className="flex-1" />
|
| 241 |
+
|
| 242 |
+
<StatusDot health={health} />
|
| 243 |
+
|
| 244 |
+
{analysisTime !== null && (
|
| 245 |
+
<span className="text-[10px]" style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)" }}>
|
| 246 |
+
{analysisTime}ms
|
| 247 |
+
</span>
|
| 248 |
+
)}
|
| 249 |
+
|
| 250 |
+
{/* API Docs link */}
|
| 251 |
+
<Link href="/swagger">
|
| 252 |
+
<button
|
| 253 |
+
className="flex items-center gap-1.5 h-7 px-2 text-xs rounded transition-colors"
|
| 254 |
+
style={{ color: "var(--text-secondary)" }}
|
| 255 |
+
onMouseEnter={(e) => (e.currentTarget.style.color = "var(--text-primary)")}
|
| 256 |
+
onMouseLeave={(e) => (e.currentTarget.style.color = "var(--text-secondary)")}
|
| 257 |
+
>
|
| 258 |
+
<BookOpen size={11} />
|
| 259 |
+
API Docs
|
| 260 |
+
</button>
|
| 261 |
+
</Link>
|
| 262 |
+
|
| 263 |
+
{/* Analyze button */}
|
| 264 |
+
<button
|
| 265 |
+
onClick={handleAnalyze}
|
| 266 |
+
disabled={isAnalyzing || health?.status !== "ok"}
|
| 267 |
+
className="flex items-center gap-1.5 h-7 px-3 text-xs font-semibold rounded transition-opacity disabled:opacity-40"
|
| 268 |
+
style={{ background: "var(--accent-blue)", color: "var(--bg-base)" }}
|
| 269 |
+
>
|
| 270 |
+
{isAnalyzing ? <Loader2 size={12} className="animate-spin" /> : <Play size={11} />}
|
| 271 |
+
{isAnalyzing ? "Analyzing…" : "Analyze"}
|
| 272 |
+
<span className="text-[9px] opacity-60 ml-0.5">⌘↵</span>
|
| 273 |
+
</button>
|
| 274 |
+
</header>
|
| 275 |
+
|
| 276 |
+
{/* Split panels */}
|
| 277 |
+
<div className="flex-1 overflow-hidden">
|
| 278 |
+
<PanelGroup direction="horizontal" className="h-full">
|
| 279 |
+
{/* Left: SQL Editor */}
|
| 280 |
+
<Panel defaultSize={50} minSize={20}>
|
| 281 |
+
<div className="flex flex-col h-full">
|
| 282 |
+
<div
|
| 283 |
+
className="flex items-center gap-2 px-3 py-1.5 flex-shrink-0"
|
| 284 |
+
style={{ borderBottom: "1px solid var(--border-muted)" }}
|
| 285 |
+
>
|
| 286 |
+
<span className="text-[10px]" style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)" }}>
|
| 287 |
+
SQL Editor
|
| 288 |
+
</span>
|
| 289 |
+
<span className="ml-auto text-[10px]" style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)" }}>
|
| 290 |
+
Ctrl+Enter to analyze
|
| 291 |
+
</span>
|
| 292 |
+
</div>
|
| 293 |
+
<div ref={editorContainerRef} className="flex-1 overflow-hidden">
|
| 294 |
+
<SqlEditor value={sql} onChange={setSql} onAnalyze={handleAnalyze} dialect={dialect} />
|
| 295 |
+
</div>
|
| 296 |
+
</div>
|
| 297 |
+
</Panel>
|
| 298 |
+
|
| 299 |
+
{/* Resize handle */}
|
| 300 |
+
<PanelResizeHandle
|
| 301 |
+
className="w-1 cursor-col-resize transition-colors"
|
| 302 |
+
style={{ background: "var(--border)" }}
|
| 303 |
+
/>
|
| 304 |
+
|
| 305 |
+
{/* Right: Results */}
|
| 306 |
+
<Panel defaultSize={50} minSize={20}>
|
| 307 |
+
<div className="flex flex-col h-full">
|
| 308 |
+
{/* Tab bar */}
|
| 309 |
+
<div
|
| 310 |
+
className="flex items-center flex-shrink-0 px-1"
|
| 311 |
+
style={{ borderBottom: "1px solid var(--border)" }}
|
| 312 |
+
>
|
| 313 |
+
{TABS.map((tab) => {
|
| 314 |
+
const badge = tab.id === "lint" ? lintBadge : tab.id === "injection" ? injectBadge : null;
|
| 315 |
+
const isActive = activeTab === tab.id;
|
| 316 |
+
return (
|
| 317 |
+
<button
|
| 318 |
+
key={tab.id}
|
| 319 |
+
onClick={() => setActiveTab(tab.id)}
|
| 320 |
+
className="relative flex items-center gap-1.5 px-3 py-2 text-xs font-medium transition-colors border-b-2"
|
| 321 |
+
style={{
|
| 322 |
+
color: isActive ? "var(--accent-blue)" : "var(--text-secondary)",
|
| 323 |
+
borderBottomColor: isActive ? "var(--accent-blue)" : "transparent",
|
| 324 |
+
}}
|
| 325 |
+
>
|
| 326 |
+
{tab.label}
|
| 327 |
+
{badge !== null && (
|
| 328 |
+
<span
|
| 329 |
+
className="text-[9px] font-bold px-1 py-0.5 rounded-full min-w-[16px] text-center"
|
| 330 |
+
style={{ background: "rgba(255,123,114,0.2)", color: "var(--accent-red)" }}
|
| 331 |
+
>
|
| 332 |
+
{badge}
|
| 333 |
+
</span>
|
| 334 |
+
)}
|
| 335 |
+
</button>
|
| 336 |
+
);
|
| 337 |
+
})}
|
| 338 |
+
</div>
|
| 339 |
+
|
| 340 |
+
{/* Tab content */}
|
| 341 |
+
<div className="flex-1 overflow-hidden">
|
| 342 |
+
{activeTab === "ast" && (
|
| 343 |
+
<AstTreeView
|
| 344 |
+
tree={parseResult?.tree ?? null}
|
| 345 |
+
tokenCount={parseResult?.token_count}
|
| 346 |
+
depth={parseResult?.depth}
|
| 347 |
+
onJumpToLine={jumpToLine}
|
| 348 |
+
/>
|
| 349 |
+
)}
|
| 350 |
+
{activeTab === "lint" && (
|
| 351 |
+
<LintPanel result={lintResult} onJumpToLine={jumpToLine} />
|
| 352 |
+
)}
|
| 353 |
+
{activeTab === "injection" && (
|
| 354 |
+
<InjectionPanel result={injectResult} onJumpToLine={jumpToLine} />
|
| 355 |
+
)}
|
| 356 |
+
{activeTab === "formatted" && (
|
| 357 |
+
<FormatterPanel result={formatResult} onApplyToEditor={setSql} />
|
| 358 |
+
)}
|
| 359 |
+
</div>
|
| 360 |
+
</div>
|
| 361 |
+
</Panel>
|
| 362 |
+
</PanelGroup>
|
| 363 |
+
</div>
|
| 364 |
+
</div>
|
| 365 |
+
);
|
| 366 |
+
}
|
frontend/src/pages/NotFound.tsx
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Link } from "wouter";
|
| 2 |
+
|
| 3 |
+
export default function NotFound() {
|
| 4 |
+
return (
|
| 5 |
+
<div className="flex flex-col items-center justify-center h-screen gap-4" style={{ background: "var(--bg-base)", color: "var(--text-primary)" }}>
|
| 6 |
+
<span style={{ fontSize: 64, opacity: 0.2 }}>/</span>
|
| 7 |
+
<h1 className="text-2xl font-semibold">404 — Page not found</h1>
|
| 8 |
+
<Link href="/" className="text-sm" style={{ color: "var(--accent-blue)" }}>
|
| 9 |
+
← Back to Analyzer
|
| 10 |
+
</Link>
|
| 11 |
+
</div>
|
| 12 |
+
);
|
| 13 |
+
}
|
frontend/src/pages/SwaggerPage.tsx
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useRef, useState } from "react";
|
| 2 |
+
import { Link } from "wouter";
|
| 3 |
+
import { ArrowLeft, Loader2 } from "lucide-react";
|
| 4 |
+
// @ts-expect-error no types
|
| 5 |
+
import SwaggerUIBundle from "swagger-ui-dist/swagger-ui-bundle.js";
|
| 6 |
+
import "swagger-ui-dist/swagger-ui.css";
|
| 7 |
+
|
| 8 |
+
export default function SwaggerPage() {
|
| 9 |
+
const containerRef = useRef<HTMLDivElement>(null);
|
| 10 |
+
const [loading, setLoading] = useState(true);
|
| 11 |
+
const [error, setError] = useState<string | null>(null);
|
| 12 |
+
|
| 13 |
+
useEffect(() => {
|
| 14 |
+
if (!containerRef.current) return;
|
| 15 |
+
|
| 16 |
+
// Determine the OpenAPI JSON URL — same origin as the page
|
| 17 |
+
const base = (import.meta.env.VITE_API_BASE as string | undefined) ?? "";
|
| 18 |
+
const url = `${base}/openapi.json`;
|
| 19 |
+
|
| 20 |
+
try {
|
| 21 |
+
SwaggerUIBundle({
|
| 22 |
+
url,
|
| 23 |
+
domNode: containerRef.current,
|
| 24 |
+
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
|
| 25 |
+
layout: "BaseLayout",
|
| 26 |
+
deepLinking: true,
|
| 27 |
+
displayRequestDuration: true,
|
| 28 |
+
filter: true,
|
| 29 |
+
tryItOutEnabled: true,
|
| 30 |
+
onComplete: () => setLoading(false),
|
| 31 |
+
});
|
| 32 |
+
setLoading(false);
|
| 33 |
+
} catch (e) {
|
| 34 |
+
setError(String(e));
|
| 35 |
+
setLoading(false);
|
| 36 |
+
}
|
| 37 |
+
}, []);
|
| 38 |
+
|
| 39 |
+
return (
|
| 40 |
+
<div className="flex flex-col h-screen" style={{ background: "var(--bg-base)", color: "var(--text-primary)" }}>
|
| 41 |
+
{/* Header */}
|
| 42 |
+
<header
|
| 43 |
+
className="flex items-center gap-3 px-4 py-2.5 flex-shrink-0"
|
| 44 |
+
style={{ borderBottom: "1px solid var(--border)", background: "var(--bg-surface)" }}
|
| 45 |
+
>
|
| 46 |
+
<Link href="/">
|
| 47 |
+
<button
|
| 48 |
+
className="flex items-center gap-1.5 h-7 px-2 text-xs rounded transition-colors"
|
| 49 |
+
style={{ color: "var(--text-secondary)" }}
|
| 50 |
+
onMouseEnter={(e) => (e.currentTarget.style.color = "var(--text-primary)")}
|
| 51 |
+
onMouseLeave={(e) => (e.currentTarget.style.color = "var(--text-secondary)")}
|
| 52 |
+
>
|
| 53 |
+
<ArrowLeft size={12} />
|
| 54 |
+
Back to Analyzer
|
| 55 |
+
</button>
|
| 56 |
+
</Link>
|
| 57 |
+
<span className="text-sm font-semibold">API Documentation</span>
|
| 58 |
+
<span
|
| 59 |
+
className="text-[10px] px-1.5 py-0.5 rounded"
|
| 60 |
+
style={{
|
| 61 |
+
background: "rgba(88,166,255,0.12)",
|
| 62 |
+
color: "var(--accent-blue)",
|
| 63 |
+
border: "1px solid rgba(88,166,255,0.25)",
|
| 64 |
+
fontFamily: "var(--font-mono)",
|
| 65 |
+
}}
|
| 66 |
+
>
|
| 67 |
+
OpenAPI 3.1
|
| 68 |
+
</span>
|
| 69 |
+
</header>
|
| 70 |
+
|
| 71 |
+
{/* Swagger UI */}
|
| 72 |
+
<div className="flex-1 overflow-auto" style={{ background: "#fff" }}>
|
| 73 |
+
{loading && (
|
| 74 |
+
<div className="flex items-center justify-center h-full gap-2" style={{ color: "#666" }}>
|
| 75 |
+
<Loader2 size={16} className="animate-spin" />
|
| 76 |
+
<span className="text-sm">Loading API docs…</span>
|
| 77 |
+
</div>
|
| 78 |
+
)}
|
| 79 |
+
{error && (
|
| 80 |
+
<div className="flex items-center justify-center h-full">
|
| 81 |
+
<p className="text-sm text-red-600">Failed to load API docs: {error}</p>
|
| 82 |
+
</div>
|
| 83 |
+
)}
|
| 84 |
+
<div ref={containerRef} className={loading ? "hidden" : ""} />
|
| 85 |
+
</div>
|
| 86 |
+
</div>
|
| 87 |
+
);
|
| 88 |
+
}
|
frontend/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2020",
|
| 4 |
+
"useDefineForClassFields": true,
|
| 5 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
| 6 |
+
"types": ["vite/client"],
|
| 7 |
+
"module": "ESNext",
|
| 8 |
+
"skipLibCheck": true,
|
| 9 |
+
"moduleResolution": "bundler",
|
| 10 |
+
"allowImportingTsExtensions": true,
|
| 11 |
+
"isolatedModules": true,
|
| 12 |
+
"moduleDetection": "force",
|
| 13 |
+
"noEmit": true,
|
| 14 |
+
"jsx": "react-jsx",
|
| 15 |
+
"strict": true,
|
| 16 |
+
"noUnusedLocals": false,
|
| 17 |
+
"noUnusedParameters": false,
|
| 18 |
+
"noFallthroughCasesInSwitch": true,
|
| 19 |
+
"baseUrl": ".",
|
| 20 |
+
"paths": {
|
| 21 |
+
"@/*": ["./src/*"]
|
| 22 |
+
}
|
| 23 |
+
},
|
| 24 |
+
"include": ["src"]
|
| 25 |
+
}
|
frontend/vite.config.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
import react from "@vitejs/plugin-react";
|
| 3 |
+
import tailwindcss from "@tailwindcss/vite";
|
| 4 |
+
import path from "path";
|
| 5 |
+
|
| 6 |
+
export default defineConfig({
|
| 7 |
+
plugins: [react(), tailwindcss()],
|
| 8 |
+
resolve: {
|
| 9 |
+
alias: {
|
| 10 |
+
"@": path.resolve(__dirname, "./src"),
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
build: {
|
| 14 |
+
// Output directly into api/static so FastAPI serves it
|
| 15 |
+
outDir: "../api/static",
|
| 16 |
+
emptyOutDir: true,
|
| 17 |
+
},
|
| 18 |
+
server: {
|
| 19 |
+
port: 5173,
|
| 20 |
+
proxy: {
|
| 21 |
+
// In dev, proxy /api and /docs to the FastAPI server
|
| 22 |
+
"/api": "http://localhost:7860",
|
| 23 |
+
"/docs": "http://localhost:7860",
|
| 24 |
+
"/openapi.json": "http://localhost:7860",
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
});
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sqlfluff>=4.0.0
|
| 2 |
+
fastapi>=0.110.0
|
| 3 |
+
uvicorn[standard]>=0.29.0
|
| 4 |
+
python-multipart>=0.0.9
|