diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..d64c0e346fffbe09e0bf6deee445ba4cc4cae84c --- /dev/null +++ b/.env.example @@ -0,0 +1,68 @@ +# Application +APP_NAME="Graph RAG Service" +DEBUG=false +ENVIRONMENT=development + +# API Server +API_HOST=0.0.0.0 +API_PORT=8000 + +# Security +# ⚠️ CRITICAL: Change SECRET_KEY before ANY deployment. +# Generate one with: python -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY=change-this-in-production-to-a-secure-random-key +ACCESS_TOKEN_EXPIRE_MINUTES=30 + +# CORS: comma-separated list of allowed origins. +# Default allows only the local Vite dev server. +# Example for production: CORS_ORIGINS=https://yourdomain.com +CORS_ORIGINS=http://localhost:3000,http://localhost:5173 + +# Neo4j +NEO4J_URI=bolt://localhost:7687 +NEO4J_USER=neo4j +NEO4J_PASSWORD=password +NEO4J_DATABASE=neo4j + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=0 + +# Celery +CELERY_BROKER_URL=redis://localhost:6379/0 +CELERY_RESULT_BACKEND=redis://localhost:6379/0 + +# LLMs +DEFAULT_LLM_PROVIDER=ollama + +# OpenAI +OPENAI_API_KEY= + +# Anthropic +ANTHROPIC_API_KEY= + +# Google Gemini +GOOGLE_API_KEY= + +# LlamaCloud (for LlamaParse) +LLAMA_CLOUD_API_KEY= +USE_LLAMA_PARSE=true + +# Ollama +OLLAMA_BASE_URL=http://localhost:11434 +OLLAMA_MODEL=llama3.1:8b +OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +# Embedding +EMBEDDING_PROVIDER=ollama +EMBEDDING_DIMENSION=768 + +# Agent Configuration +MAX_AGENT_ITERATIONS=5 +AGENT_TIMEOUT_SECONDS=30 + +# Observability +ENABLE_TRACING=true +ENABLE_METRICS=true +LOG_LEVEL=INFO diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..636f5c34649909eefe8422eaa0d49d7d9d0a303d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text diff --git a/.github/prompts/Solution_Architecture.prompt.md b/.github/prompts/Solution_Architecture.prompt.md new file mode 100644 index 0000000000000000000000000000000000000000..852cda4f1deecc1823104cb7ef6d7e9cc1aae152 --- /dev/null +++ b/.github/prompts/Solution_Architecture.prompt.md @@ -0,0 +1,169 @@ +# Solution Architecture: Agentic Graph RAG as a Service + +This document outlines a detailed technical approach to building the Agentic Graph RAG platform. It focuses on modularity, scalability, and the specific requirements of the Lyzr Hackathon, with a strong emphasis on production-grade robustness. + +## 1. High-Level Architecture + +The system is designed as a set of modular services centered around a shared Knowledge Graph and Vector Store, fortified with enterprise-grade security and observability layers. + +```mermaid +graph TD + User[User / Client] --> Auth[Auth & Access Control] + Auth --> API[Unified API Gateway] + + subgraph "Observability Layer (OpenTelemetry)" + Logs[Structured Logging] + Traces[Agent Traces] + Metrics[Performance Metrics] + end + + API -.-> Logs & Traces & Metrics + + subgraph "Ingestion Pipeline (Async Workers)" + API --> Queue[Task Queue (Redis/Celery)] + Queue --> Ingest[Ingestion Worker] + Ingest --> Chunking[Text Chunking] + Chunking --> OntologyGen[LLM Ontology Gen (Versioned)] + OntologyGen --> Extract[Entity & Relation Extraction] + Extract --> Resolution[Entity Resolution / Dedup] + Resolution --> GraphDB[(Neo4j / Neptune)] + Resolution --> VectorDB[(Vector Store)] + end + + subgraph "Retrieval Context" + API --> Agent[Agent Orchestrator] + Agent --> Decomp[Query Decomposer] + Decomp --> Router[Query Router / Planner] + + Router --> |Semantic Query| VectorSearch[Vector Search] + Router --> |Deep Relation| GraphSearch[Graph Traversal / Cypher] + Router --> |Structured| FilterSearch[Metadata Filter] + + VectorSearch & GraphSearch & FilterSearch --> Validator[Hallucination Guard / Schema Validator] + Validator --> Synthesizer[Response Synthesizer] + Synthesizer --> Agent + end +``` + +## 2. Technology Stack Selection + +* **Language:** Python 3.12 (Standard for AI/ML engineering). +* **API Framework:** FastAPI (Async support, auto-documentation). +* **Orchestration:** LlamaIndex (Preferred for Graph RAG). +* **LLM:** multi-LLM support like ollama, open ai, gemini,claude (use lang-graph) (Reasoning & Extraction) & `BAAI bge-m3` this model is available on ollama so we will use from ollama (Embeddings). +* **Graph Database:** Neo4j (Primary) . +* **Vector Store:** Neo4j Vector Index (for unified storage) or Qdrant/Chroma. +* **Task Queue:** Celery with Redis (for async ingestion). +* **Monitoring:** OpenTelemetry + Prometheus/Grafana. +* **Frontend:** React vite + tailwind css (for Visual Ontology Editor). + +## 3. Production-Grade Components + +### A. Document-to-Graph Pipeline (Ingestion) + +This pipeline converts unstructured text into a structured Knowledge Graph, robust to schema changes and duplicates. + +1. **Ontology Generation & Evolution:** + * *Initial:* Ask LLM to identify high-level concepts (nodes) and interactions (edges) from first $N$ chunks. + * *Visual Editor:* Human approval step to refine the JSON schema. + * **Drift Handling:** Incorporate an "Ontology Versioning" system. Every node/edge is tagged with `ontology_version: v1.0`. New documents causing schema changes trigger a "Migration Proposal" for approval. + +2. **Extraction & Embedding:** + * **Prompt Engineering:** "Given text + Ontology v1.0, extract entities/relationships." + * **Hybrid Nodes:** Create `(:Chunk)` nodes linked to `(:Entity)` nodes (`(:Chunk)-[:MENTIONS]->(:Entity)`). This preserves ground truth source text alongside abstract graph relationships. + +3. **Advanced Entity Resolution:** + * *Naive:* Exact string match. + * *Production:* Multi-stage blocking and merging. + 1. **Blocking:** Group entities by Label and similar name (e.g., phonetic match). + 2. **Semantic Check:** Compare embeddings of candidates. + 3. **Threshold:** If similarity > 0.95 -> Auto-merge. If 0.85-0.95 -> Flag for "Human Review Queue". + +### B. The Agentic Retrieval System (The Brain) + +A state machine loop designed for accuracy and fail-safe operation. + +**1. Query Decomposition & Routing** +Instead of a single step, the Agent breaks down complexity: +* *User Query:* "How is the CEO of Lyzr related to OpenAI?" +* *Decomposition:* + 1. "Identify Lyzr CEO" (Vector/Graph lookup) -> *Result: user_X* + 2. "Find path between user_X and OpenAI" (Graph traversal). +* *Router:* Dynamically selects tools for each sub-step. + +**2. Tool Implementation with Guardrails:** +* **Vector Tool:** Top-k retrieval using embedding similarity. +* **Graph Tool (Text-to-Cypher):** Uses LLM to generate Cypher. + * **Hallucination Guard:** The tool injects the *strict* allowed schema into the prompt. Generated Cypher is parsed and validated against a "Relationship Whitelist" before execution to prevent schema injection or invalid edge types. +* **Filter Tool:** Converts natural language to structured DB filters (WHERE clauses). + +**3. Latency & Performance Strategy:** +* **Timeouts:** Hard limit on agent reasoning steps (e.g., max 5 loops). +* **Fallback:** If Graph tool fails or times out, degrade gracefully to pure Vector Search for a "best effort" answer. + +### C. Parity & Extensibility Layer + +We define abstract base class interfaces to ensure no vendor lock-in. + +```python +class GraphStore(ABC): + @abstractmethod + def execute_query(self, query: str, params: dict): pass + +class VectorStore(ABC): + @abstractmethod + def search(self, query_vector: List[float], k: int): pass + +class LLMProvider(ABC): + @abstractmethod + def complete(self, prompt: str): pass + +# Implementations: Neo4jStore, NeptuneStore, QdrantStore, OpenAIProvider, etc. +``` + +## 4. Scalability, Security & Observability + +To meet "Production-Grade" criteria, these non-functional requirements are critical: + +1. **Access Control (RBAC):** + * Pre-retrieval enforcement. + * All queries filter by `user.tenant_id` or `user.permissions` to ensure users only retrieve data they are authorized to see. + +2. **Observability:** + * **Tracing:** Log every step of the Agent's reasoning chain (Input -> Decomp -> Tool Call -> Result). This is vital for debugging "why did the bot say that?". + * **Metrics:** Track Token Usage, Latency p95, and Cache Hit Rates. + +3. **Async Ingestion:** + * Ingestion is decoupled from the user request loop. + * File Upload API -> Pushes ID to Redis Queue -> Background Worker picks up -> Runs Extraction -> Updates Graph. + +4. **Caching Strategy:** + * **Semantic Cache (Redis):** Before hitting the LLM, check if a semantically similar query has been answered recently. reduces cost and latency. + * **Embedding Cache:** Store computed embeddings to avoid re-calculation for identical text chunks. + +## 5. Implementation Plan + +### Phase 1: Foundation (Hours 1-4) +1. Set up Repository, Python envf (Neo4j/Redis). +2. Implement `GraphStore` & `VectorStore` abstractions. +3. Create Basic Auth & Middleware logging. + +### Phase 2: Ingestion Engine (Hours 5-12) +1. Implement PDF extractor & Async Worker skeleton. +2. Build "Ontology Proposer" & "Graph Extractor" prompts. +3. Implement Entity Resolution logic. + +### Phase 3: The Retrieval Agent (Hours 13-20) +1. Set up Agent loop with Query Decomposition. +2. Implement `Text2Cypher` with schema validation. +3. Implement Latency Timeouts & Fallbacks. + +### Phase 4: Refinement & UI (Hours 21-24) +1. Build Visual Editor (Streamlit). +2. Add simple Evaluation Script (run known queries, check answers). +3. Write `README.md` highlighting the "Production Thinking" (RBAC, Async, Observability). + +## 6. Key Innovations +1. **Hybrid Chunk Nodes:** Storing source text explicitly in the graph for ground-truth verification. +2. **Self-Correcting Cypher:** If Cypher execution fails, feed the error back to the LLM to fix syntax automatically. +3. **Adaptive Retrieval:** The agent assigns a "confidence score" to each retrieval method. If Vector Search confidence is low (<0.7), it automatically triggers Graph Traversal to boost context. diff --git a/.github/workflows/sync_to_hf.yml b/.github/workflows/sync_to_hf.yml new file mode 100644 index 0000000000000000000000000000000000000000..3eed44b7f285b47df90f3420ce4150df0912da34 --- /dev/null +++ b/.github/workflows/sync_to_hf.yml @@ -0,0 +1,33 @@ +name: Sync to Hugging Face +on: + push: + branches: [master] + +jobs: + sync-to-hub: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Push to hub + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + # 1. Turn on Large File Storage (LFS) + git lfs install + + # 2. Tell Hugging Face to properly handle images + echo "*.png filter=lfs diff=lfs merge=lfs -text" >> .gitattributes + echo "*.jpg filter=lfs diff=lfs merge=lfs -text" >> .gitattributes + echo "*.ico filter=lfs diff=lfs merge=lfs -text" >> .gitattributes + + # 3. Wipe the hidden git history in the runner to clear past image errors + rm -rf .git + git config --global user.email "action@github.com" + git config --global user.name "GitHub Action" + + # 4. Create a fresh, clean package and force push it + git init + git checkout -b main + git add . + git commit -m "Automated sync to Hugging Face" + git push --force https://anky2002:$HF_TOKEN@huggingface.co/spaces/anky2002/graph-rag main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..701c7f9fbb55bb9e513a1df419bbb8b7b9266b96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,99 @@ +# ── Python ─────────────────────────────────────────────────────────────────── +__pycache__/ +*.py[oc] +*.pyo +*.pyd +build/ +dist/ +wheels/ +*.egg-info/ +.eggs/ +*.egg + +# ── Virtual environments ────────────────────────────────────────────────────── +.venv/ +venv/ +ENV/ +env/ + +# ── Environment / Secrets (NEVER commit real keys) ──────────────────────────── +.env +.env.local +.env.*.local +*.secret + +# ── Node.js / NPM ───────────────────────────────────────────────────────────── +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.npm/ + +# ── Uploaded user data ──────────────────────────────────────────────────────── +data/uploads/* +!data/uploads/.gitkeep + +# ── One-off debug / experiment test files ───────────────────────────────────── +test_combinations.py +test_document_embedding.py +test_embedding.py +test_exact_combination.py +test_narrow_down.py +test_nomic.py +test_resume_text.py + +# ── IDE / OS ────────────────────────────────────────────────────────────────── +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +Thumbs.db + +# ── Logs & temp ─────────────────────────────────────────────────────────────── +*.log +*.tmp +*.temp +htmlcov/ +.coverage +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ + +# ── Docs that are project-internal notes (not needed in repo) ───────────────── +BGE_M3_ISSUE_ANALYSIS.md +CONFIGURATION_CHANGES.md +PROJECT_COMPLETION_CHECKLIST.md +LLAMAPARSE_SETUP.md + +# ── Lyzr Hackathon source material (not part of the submitted codebase) ──────── +Lyzr_Hackathon_Problem_Statement.md + +# ── UV lock / python version pins (repo-specific, not portable) ───────────────── +# uv.lock # keep: ensures reproducible installs for reviewers +.python-version + +# ── pyvis / notebook temp files ────────────────────────────────────────────── +*.html +!frontend-react/index.html +!frontend-react/*.html +!frontend/*.html + +# ── Celery / task artefacts ─────────────────────────────────────────────────── +celerybeat-schedule +celerybeat.pid + +# ── Neo4j local data (if ever mounted) ─────────────────────────────────────── +neo4j_data/ + +# ── Windows shortcuts / artifacts ──────────────────────────────────────────── +*.lnk + +# ── Generated graphify analysis artifacts (large, auto-generated) ───────────── +artifacts/graphify-out/ +src/graphify-out/ + +# ── Benchmark result outputs ────────────────────────────────────────────────── +benchmarks/results/ +benchmarks/*.json diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000000000000000000000000000000000000..82edb72a9c4442125e80fb753ff3c1765b3b0125 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,297 @@ +# Graph RAG Service - Project Documentation + +## System Architecture + +### Overview +The Graph RAG Service is built as a modular, production-grade platform with the following key components: + +1. **API Gateway (FastAPI)**: Handles all HTTP requests, authentication, and routing +2. **Ingestion Pipeline**: Processes documents and constructs knowledge graphs +3. **Retrieval Agent (LangGraph)**: Intelligent query routing and response synthesis +4. **Storage Layer**: Neo4j for graph + vector storage +5. **Task Queue**: Celery + Redis for async processing +6. **Observability**: OpenTelemetry for tracing and metrics + +### Design Principles + +#### 1. No Vendor Lock-in +All core components are abstracted behind interfaces: +- `GraphStore`: Can swap Neo4j for AWS Neptune +- `VectorStore`: Supports multiple vector databases +- `LLMProvider`: Works with any LLM (OpenAI, Anthropic, Gemini, Ollama) + +#### 2. Production-Ready +- **Async Processing**: Non-blocking I/O for all database operations +- **Background Jobs**: Celery workers handle heavy ingestion tasks +- **Authentication**: JWT-based with RBAC support +- **Error Handling**: Graceful degradation and fallback mechanisms +- **Observability**: Full tracing and metrics collection + +#### 3. Intelligent Retrieval +The agentic system: +- Decomposes complex queries into sub-queries +- Dynamically selects retrieval methods (vector vs graph vs cypher) +- Validates outputs against schema (hallucination guard) +- Provides reasoning chains for transparency + +## Components Deep Dive + +### Core Abstractions (`src/graph_rag_service/core/`) + +#### GraphStore Interface +```python +class GraphStore(ABC): + @abstractmethod + async def create_node(entity: Entity) -> str + @abstractmethod + async def create_relationship(relationship: Relationship) -> str + @abstractmethod + async def execute_query(query: str, params: dict) -> List[dict] + @abstractmethod + async def find_path(source: str, target: str, max_depth: int) -> List[dict] +``` + +Implementation: `Neo4jStore` provides unified graph + vector storage using Neo4j 5.x vector capabilities. + +#### LLMProvider Interface +```python +class LLMProvider(ABC): + @abstractmethod + async def complete(prompt: str, **kwargs) -> str + @abstractmethod + async def embed(text: str) -> List[float] +``` + +Implementation: `UnifiedLLMProvider` wraps OpenAI, Anthropic, Gemini, and Ollama with a consistent interface. + +#### Entity Resolution +Multi-stage resolution: +1. **Blocking**: Group by entity type and name similarity (fast reject) +2. **Semantic Check**: Compare embeddings for deep similarity +3. **Threshold Matching**: Configurable thresholds (0.85 default) +4. **Auto-merge**: High confidence merges (>0.95) +5. **Human Review Queue**: Medium confidence flagged for review (0.85-0.95) + +### Ingestion Pipeline (`src/graph_rag_service/ingestion/`) + +#### Flow +1. **Document Processing**: Extract text from PDF/TXT/MD/DOCX +2. **Chunking**: Split into overlapping chunks (1024 tokens, 200 overlap) +3. **Ontology Generation**: LLM analyzes samples to propose entity/relationship types +4. **Entity Extraction**: Extract entities and relationships per chunk +5. **Entity Resolution**: Deduplicate and merge entities +6. **Embedding Generation**: Create vector embeddings (BGE-M3) +7. **Graph Construction**: Store in Neo4j with hybrid nodes + +#### Hybrid Nodes +Each chunk is stored as both: +- A `(:Chunk)` node with text and embedding +- Connected to `(:Entity)` nodes via `[:MENTIONS]` relationships + +This preserves source text for grounding while enabling abstract graph queries. + +### Retrieval System (`src/graph_rag_service/retrieval/`) + +#### Tools +1. **VectorSearchTool**: Semantic similarity using embeddings +2. **GraphTraversalTool**: Relationship exploration and path finding +3. **CypherGenerationTool**: Text-to-Cypher with validation +4. **MetadataFilterTool**: Structured queries on attributes + +#### Agent Workflow (LangGraph) +``` +[Query] → [Decompose] → [Route] → [Vector/Graph/Cypher] → [Synthesize] → [Response] + ↑ ↓ + └─────────────────────────────────────┘ + (Iterative refinement) +``` + +#### Hallucination Guards +- **Schema Injection**: Prompt includes allowed entity/relationship types +- **Cypher Validation**: Parse and validate against whitelist +- **Self-Correction**: Feed errors back to LLM to fix syntax +- **Fallback**: If graph fails, degrade to vector search + +### API Layer (`src/graph_rag_service/api/`) + +#### Endpoints +- `POST /api/auth/login`: Get JWT token +- `POST /api/documents/upload`: Upload document (returns task ID) +- `GET /api/documents/status/{task_id}`: Check ingestion progress +- `POST /api/query`: Execute agentic query +- `GET /api/ontology`: Get current ontology schema +- `PUT /api/ontology`: Update ontology (admin only) +- `GET /api/graph/visualization`: Get graph data for visualization +- `GET /api/system/health`: System health check +- `GET /api/system/stats`: System statistics + +#### Authentication +- JWT tokens with configurable expiration (default: 30 min) +- RBAC with scopes: `read`, `write`, `admin` +- Dependency injection for protected endpoints + +### Workers (`src/graph_rag_service/workers/`) + +#### Celery Tasks +- `ingest_document`: Process single document +- `ingest_documents_batch`: Process multiple documents +- `health_check`: Worker health verification + +#### Configuration +- Broker: Redis +- Result Backend: Redis +- Serializer: JSON +- Task timeout: 1 hour (configurable) + +### Observability (`src/graph_rag_service/observability/`) + +#### OpenTelemetry Integration +- **Traces**: Agent reasoning steps, tool calls, database queries +- **Metrics**: + - `documents_ingested`: Counter + - `queries_executed`: Counter + - `query_duration_seconds`: Histogram + - `entities_extracted`: Counter + +#### Structured Logging +- Log level: INFO (configurable) +- Format: `%(asctime)s - %(name)s - %(levelname)s - %(message)s` +- All async operations logged with context + +## Configuration + +### Environment Variables +Key settings in `.env`: +- **Neo4j**: `NEO4J_URI`, `NEO4J_USER`, `NEO4J_PASSWORD` +- **Redis**: `REDIS_HOST`, `REDIS_PORT` +- **LLM Provider**: `DEFAULT_LLM_PROVIDER` (openai/anthropic/gemini/ollama) +- **API Keys**: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY` +- **Ollama**: `OLLAMA_BASE_URL`, `OLLAMA_MODEL`, `OLLAMA_EMBEDDING_MODEL` +- **Security**: `SECRET_KEY`, `ACCESS_TOKEN_EXPIRE_MINUTES` + +### Tuning Parameters +- `CHUNK_SIZE`: 1024 (text chunk size) +- `CHUNK_OVERLAP`: 200 (overlap between chunks) +- `MAX_AGENT_ITERATIONS`: 5 (max reasoning steps) +- `AGENT_TIMEOUT_SECONDS`: 30 (query timeout) +- `ENTITY_RESOLUTION_THRESHOLD`: 0.85 (similarity threshold) +- `DEFAULT_TOP_K`: 5 (retrieval results) +- `GRAPH_MAX_DEPTH`: 3 (graph traversal depth) + +## Deployment + +### Local Development +```bash +# 1. Ensure Neo4j and Redis are running +# 2. Configure .env with connection details + +# 3. Start API server +./start-server.sh # or start-server.bat on Windows + +# 4. Start workers +./start-worker.sh # or start-worker.bat on Windows +``` + +### Production Considerations +1. **Database**: Use managed Neo4j (Aura) or self-hosted cluster +2. **Redis**: Use managed Redis (AWS ElastiCache, Redis Cloud) +3. **Worker Scaling**: Add more Celery workers based on ingestion load +4. **API Scaling**: Run multiple API instances behind load balancer +5. **Monitoring**: Integrate with Prometheus/Grafana for metrics +6. **Secrets**: Use secret management (AWS Secrets Manager, HashiCorp Vault) + +## Extensibility + +### Adding New LLM Provider +1. Implement `LLMProvider` interface +2. Add to `LLMFactory.create()` method +3. Update config with new provider settings + +### Adding New Graph Database +1. Implement `GraphStore` interface +2. Update `IngestionPipeline` to use new store +3. Test with existing workflows + +### Custom Retrieval Tools +1. Create new tool class with `run()` method +2. Add to `AgentRetrievalSystem.tools` +3. Update routing logic in `_route_query()` + +## Testing Strategy + +### Unit Tests +- Test each component independently +- Mock external dependencies (Neo4j, Redis, LLMs) +- Focus on business logic + +### Integration Tests +- Test component interactions +- Use test database instances +- Verify end-to-end flows + +### Performance Tests +- Benchmark ingestion throughput +- Measure query latencies +- Stress test with concurrent requests + +## Future Enhancements + +### Phase 1 (Current MVP) +- ✅ Core ingestion pipeline +- ✅ Agentic retrieval system +- ✅ Multi-LLM support +- ✅ Entity resolution +- ✅ Async workers + +### Phase 2 (Next Steps) +- [ ] React frontend with visual ontology editor +- [ ] Graph visualization (D3.js/Cytoscape) +- [ ] Advanced ontology evolution with migrations +- [ ] Semantic cache with Redis +- [ ] Batch ingestion optimization + +### Phase 3 (Advanced Features) +- [ ] Multi-tenant support with data isolation +- [ ] Fine-tuned entity extraction models +- [ ] Graph neural network embeddings +- [ ] Automated ontology quality metrics +- [ ] Export/import ontology schemas + +## Troubleshooting + +### Common Issues + +#### Neo4j Connection Failed +- Verify Neo4j is running and accessible +- Verify credentials in `.env` +- Try connecting with cypher-shell: `cypher-shell -u neo4j -p password` + +#### Celery Worker Not Processing +- Check Redis is running: `redis-cli ping` +- Verify broker URL in `.env` +- Check worker logs + +#### Ollama Models Not Found +- Pull models: `ollama pull llama3.2 && ollama pull bge-m3` +- Verify Ollama is running: `curl http://localhost:11434/api/tags` + +#### Query Returns No Results +- Verify documents are ingested: `GET /api/system/stats` +- Check ontology exists: `GET /api/ontology` +- Try simpler queries first + +## Support + +For issues or questions: +1. Check documentation and troubleshooting guide +2. Search existing GitHub issues +3. Open new issue with: + - Clear description + - Steps to reproduce + - Environment details + - Relevant logs + +--- + +**Last Updated**: February 2026 +**Version**: 0.1.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..ab90f3daf214eea14daa338912c442bef12a68c3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,155 @@ +FROM ubuntu:22.04 + +# Avoid tzdata interactive prompts +ENV DEBIAN_FRONTEND=noninteractive + +# Install system dependencies and Python 3.12 +RUN apt-get update && apt-get install -y software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update && apt-get install -y \ + python3.12 \ + python3.12-venv \ + python3.12-dev \ + python3-pip \ + curl \ + wget \ + git \ + redis-server \ + openjdk-17-jdk \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js for frontend build +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs + +# Create app directory +WORKDIR /app + +# Download and extract Neo4j and GDS plugin +RUN wget -q https://neo4j.com/artifact.php?name=neo4j-community-5.18.0-unix.tar.gz -O neo4j.tar.gz \ + && tar -xf neo4j.tar.gz \ + && mv neo4j-community-5.18.0 neo4j \ + && rm neo4j.tar.gz \ + && wget -q https://github.com/neo4j/graph-data-science/releases/download/2.6.4/neo4j-graph-data-science-2.6.4.jar -O neo4j/plugins/neo4j-graph-data-science.jar + +# Configure Neo4j for demo mode (disable auth, limit memory, enable GDS) +RUN echo "dbms.security.auth_enabled=false" >> neo4j/conf/neo4j.conf \ + && echo "server.memory.heap.initial_size=512m" >> neo4j/conf/neo4j.conf \ + && echo "server.memory.heap.max_size=1G" >> neo4j/conf/neo4j.conf \ + && echo "server.memory.pagecache.size=1G" >> neo4j/conf/neo4j.conf \ + && echo "dbms.security.procedures.unrestricted=gds.*" >> neo4j/conf/neo4j.conf + +# Copy project files +COPY . . + +# Build frontend +WORKDIR /app/frontend-react +RUN npm install +RUN npm run build + +# Setup Python backend +WORKDIR /app +RUN python3.12 -m venv .venv +ENV PATH="/app/.venv/bin:$PATH" +RUN pip install --upgrade pip +RUN pip install -e . + +# Create start script +RUN echo '#!/bin/bash\n\ +\n\ +# Start Redis\n\ +redis-server --daemonize yes\n\ +\n\ +# Start Neo4j in background\n\ +/app/neo4j/bin/neo4j start\n\ +\n\ +# Wait for Neo4j to be ready\n\ +echo "Waiting for Neo4j to start..."\n\ +while ! curl -s http://localhost:7474 > /dev/null; do\n\ + sleep 2\n\ +done\n\ +echo "Neo4j HTTP is up! Waiting for Bolt (7687)..."\n\ +while ! (echo > /dev/tcp/localhost/7687) >/dev/null 2>&1; do\n\ + sleep 2\n\ +done\n\ +echo "Neo4j is up!"\n\ +\n\ +# Set environment variables for Demo Mode\n\ +export NEO4J_URI=bolt://localhost:7687\n\ +export NEO4J_USER=neo4j\n\ +export NEO4J_PASSWORD=dummy\n\ +export REDIS_HOST=localhost\n\ +export REDIS_PORT=6379\n\ +export REDIS_DB=0\n\ +export DEMO_MODE=true\n\ +export ENVIRONMENT=production\n\ +export SECRET_KEY=demo-secret-key-1234567890\n\ +\n\ +if [ -z "$GOOGLE_API_KEY" ]; then\n\ + export DEFAULT_LLM_PROVIDER=mock\n\ + export EMBEDDING_PROVIDER=mock\n\ + export DEMO_MODE=true\n\ + echo "[WARNING] GOOGLE_API_KEY is not set. Running in DEMO_MODE with mock LLM provider."\n\ +else\n\ + export DEFAULT_LLM_PROVIDER=gemini\n\ + export EMBEDDING_PROVIDER=gemini\n\ +fi\n\ +\n\ +# Create default admin user in Neo4j\n\ +python -c "\n\ +import asyncio\n\ +from src.graph_rag_service.core.neo4j_store import Neo4jStore\n\ +from src.graph_rag_service.api.auth import get_password_hash\n\ +\n\ +async def main():\n\ + store = Neo4jStore()\n\ + await store.connect()\n\ + try:\n\ + await store.create_user({\n\ + '\''username'\'': '\''admin'\'',\n\ + '\''hashed_password'\'': get_password_hash('\''admin'\''),\n\ + '\''email'\'': '\''admin@example.com'\'',\n\ + '\''full_name'\'': '\''Demo Admin'\'',\n\ + '\''disabled'\'': False,\n\ + '\''scopes'\'': ['\''read'\'', '\''write'\'', '\''admin'\''],\n\ + '\''tenant_id'\'': '\''demo_tenant'\'',\n\ + })\n\ + print('\''Admin user created in Neo4j'\'')\n\ + \n\ + # Check GDS\n\ + gds_res = await store.execute_query('\''RETURN gds.version() as version'\'')\n\ + print(f'\''GDS Plugin Version: {gds_res[0]["version"] if gds_res else "NOT FOUND"}'\'')\n\ + except Exception as e:\n\ + print(f'\''Admin user creation note: {e}'\'')\n\ + raise e\n\ + await store.disconnect()\n\ +\n\ +asyncio.run(main())\n\ +"\n\ +if [ $? -ne 0 ]; then\n\ + echo "Admin seed failed, retrying in 5 seconds..."\n\ + sleep 5\n\ + python -c "\n\ +import asyncio\n\ +from src.graph_rag_service.core.neo4j_store import Neo4jStore\n\ +from src.graph_rag_service.api.auth import get_password_hash\n\ +\n\ +async def main():\n\ + store = Neo4jStore()\n\ + await store.connect()\n\ + try:\n\ + await store.create_user({'username': 'admin', 'hashed_password': get_password_hash('admin'), 'scopes': ['read', 'write', 'admin'], 'tenant_id': 'demo_tenant'})\n\ + except Exception as e: pass\n\ + await store.disconnect()\n\ +asyncio.run(main())\n\ + "\n\ +fi\n\ +\n\ +# Start FastAPI and serve static files (frontend)\n\ +uvicorn src.graph_rag_service.api.server:app --host 0.0.0.0 --port 7860\n\ +' > start.sh && chmod +x start.sh + +# HF Spaces requires serving on 7860 +EXPOSE 7860 + +CMD ["./start.sh"] diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000000000000000000000000000000000000..eebcff593be3f84721c43243a27f45aa5fee7cb0 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,265 @@ +# Quick Start Guide + +Get the Graph RAG Service up and running in 10 minutes! + +## Prerequisites + +Before starting, make sure you have: +- Python 3.12 or higher +- Neo4j database (running locally or remotely) +- Redis server (running locally or remotely) +- UV package manager (will be installed if missing) + +## Step 1: Clone and Setup + +```bash +cd graph-RAG + +# Install UV if you don't have it +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install dependencies +uv sync +``` + +## Step 2: Configure Environment + +```bash +# Copy environment template +cp .env.example .env + +# Edit .env with your settings +# Configure NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD +# Configure REDIS_URL +``` + +## Step 3: Ensure Backend Services Are Running + +Make sure Neo4j and Redis are running and accessible: +- Neo4j should be available at the URI specified in your .env file (default: bolt://localhost:7687) +- Redis should be available at the URL specified in your .env file (default: redis://localhost:6379) + +## Step 4: Start the API Server + +### On Windows: +```bash +start-server.bat +``` + +### On Mac/Linux: +```bash +chmod +x start-server.sh +./start-server.sh +``` + +### Or directly (any OS): +```bash +uv run python main.py +``` + +The API server will start on `http://localhost:8000` + +## Step 5: Start Celery Worker + +For asynchronous document ingestion, start a worker in a **new terminal**: + +### On Windows: +```bash +start-worker.bat +``` + +### On Mac/Linux: +```bash +chmod +x start-worker.sh +./start-worker.sh +``` + +### Or directly: +```bash +uv run celery -A src.graph_rag_service.workers.celery_worker worker --loglevel=info +``` + +## Step 6: Start the React Frontend + +In a **new terminal**: + +```bash +cd frontend-react + +# Install Node dependencies (first time only) +npm install + +# Start the dev server +npm run dev +``` + +The React UI will open at `http://localhost:5173` + +> **Note:** The project uses a **React/Vite** frontend (`frontend-react/`). +> There is no `streamlit run app.py` — any references to Streamlit in older docs are outdated. + +Login with: **username** `admin` / **password** `admin` + +## Step 7: Test the API + +### Using cURL + +1. **Get Access Token** +```bash +curl -X POST "http://localhost:8000/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username": "demo", "password": "demo"}' +``` + +Save the `access_token` from the response. + +2. **Upload a Document** +```bash +curl -X POST "http://localhost:8000/api/documents/upload" \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ + -F "file=@sample.pdf" +``` + +3. **Query the Knowledge Base** +```bash +curl -X POST "http://localhost:8000/api/query" \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "query": "What is this document about?", + "top_k": 5 + }' +``` + +### Using the Interactive Docs + +Open your browser and go to: +- Swagger UI: `http://localhost:8000/docs` +- ReDoc: `http://localhost:8000/redoc` + +Click "Authorize" and enter your access token to test endpoints interactively. + +## Step 8: Setup Ollama (Optional) + +If you want to use local LLMs instead of OpenAI/Anthropic: + +```bash +# Install Ollama (https://ollama.ai) +# Then pull models: +ollama pull llama3.2 +ollama pull bge-m3 +``` + +Update `.env`: +``` +DEFAULT_LLM_PROVIDER=ollama +OLLAMA_BASE_URL=http://localhost:11434 +OLLAMA_MODEL=llama3.2 +OLLAMA_EMBEDDING_MODEL=bge-m3 +``` + +## Common Commands + +### Check System Health +```bash +curl http://localhost:8000/api/system/health +``` + +### Get System Stats +```bash +curl -H "Authorization: Bearer YOUR_TOKEN" \ + http://localhost:8000/api/system/stats +``` + +### View Ontology +```bash +curl -H "Authorization: Bearer YOUR_TOKEN" \ + http://localhost:8000/api/ontology +``` + +### Visualize Graph +```bash +curl -H "Authorization: Bearer YOUR_TOKEN" \ + "http://localhost:8000/api/graph/visualization?limit=50" +``` + +## Troubleshooting + +### Neo4j Connection Error +```bash +# Verify credentials in .env +# Check Neo4j is accessible + +# Access Neo4j browser +open http://localhost:7474 + +# Try connecting with cypher-shell +cypher-shell -u neo4j -p password +``` + +### Redis Connection Error +```bash +# Check Redis is running +redis-cli ping + +# Test Redis connection +redis-cli -h localhost -p 6379 ping +``` + +### Worker Not Processing +```bash +# Check worker logs +# If running locally: +celery -A src.graph_rag_service.workers.celery_worker inspect active +``` + +### API Server Won't Start +```bash +# Check for port conflicts +lsof -i :8000 # On Mac/Linux +netstat -ano | findstr :8000 # On Windows + +# View detailed logs +uv run python main.py +``` + +## Next Steps + +- Read the [README.md](README.md) for comprehensive documentation +- Check [ARCHITECTURE.md](ARCHITECTURE.md) for system design details +- Explore the API docs at `http://localhost:8000/docs` + +## Using with Different LLM Providers + +### OpenAI +```bash +# In .env: +DEFAULT_LLM_PROVIDER=openai +OPENAI_API_KEY=sk-your-key-here +``` + +### Anthropic +```bash +# In .env: +DEFAULT_LLM_PROVIDER=anthropic +ANTHROPIC_API_KEY=sk-ant-your-key-here +``` + +### Google Gemini +```bash +# In .env: +DEFAULT_LLM_PROVIDER=gemini +GOOGLE_API_KEY=your-key-here +``` + +Services will be available at: +- API: `http://localhost:8000` +- Neo4j Browser: `http://localhost:7474` + +## Need Help? + +- Check the troubleshooting section above +- Read the full [README.md](README.md) +- Open an issue on GitHub + +Happy building! 🚀 diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..18d71ca47a2648aee008df533e4adb8549383482 --- /dev/null +++ b/README.md @@ -0,0 +1,467 @@ +# CORTEX — Agentic Graph RAG Platform + +> **CORTEX** is a production-grade, agentic Knowledge Graph platform that transforms unstructured documents and web content into an intelligent, queryable knowledge graph — with a full-featured React UI, streaming AI chat, real-time graph visualization, simulation personas, and deep ontology governance. + +--- + +## ✨ What's Been Built + +### 🖥️ Full-Stack Application + +| Layer | Stack | +|---|---| +| **Backend API** | FastAPI (async) + Python 3.12 | +| **Task Queue** | Celery + Redis | +| **Graph + Vector DB** | Neo4j 5.x (unified) | +| **LLM Layer** | OpenAI, Anthropic, Google Gemini, Ollama | +| **Frontend** | React 18 + TypeScript + Vite | +| **Unified Start** | `npm run rag` (concurrently launches all 3 processes) | + +--- + +## 🚀 Features + +### 📥 Document Ingestion Pipeline + +- **Multi-format ingestion**: PDF, TXT, MD, DOCX, CSV, XLSX, PPTX, JSON +- **Web scraping**: Single-page scrape via `POST /api/documents/scrape` +- **Deep web crawling**: Multi-depth Playwright-powered crawler (`POST /api/documents/crawl`) via Crawl4AI +- **Async Celery workers**: Upload returns instantly with a `task_id`; background workers build the graph +- **Re-ingest**: Admin can trigger re-processing of any stored document +- **Document preview & download**: In-browser preview of text/Markdown; PDF download via API + +### 🔭 Ontology Management + +- **Auto-generation**: LLM analyzes document chunks to propose entity types & relationship types +- **LLM-powered refinement**: `POST /api/ontology/refine` — refine schema with optional human feedback +- **Versioning**: Each schema change bumps the version (`v1.0` → `v1.1`, etc.) +- **Document-scoped stats**: `/api/ontology/stats?document_id=...` returns entity/relationship breakdowns for a specific document +- **Visual editor**: Ontology view in UI with editable entity types and relationship types +- **Ontology Drift Detection**: Automated drift detection compares live graph against new chunk samples; exposes pending/approved/rejected drift reports with admin approve/reject workflow + +### 🤖 Agentic Retrieval System + +- **LangGraph orchestration**: State-machine ReACT agent with multi-step reasoning and fallback mechanisms +- **Tool routing**: Dynamically selects from Naive Vector, Hybrid, Metadata Filtering, Global Community Search, HippoRAG, Graph of Thoughts (GoT), and Cypher. +- **Retrieval Modes**: Switch seamlessly between `AUTO`, `HYBRID`, `HIPPO-RAG`, `LOCAL GRAPH`, `GLOBAL COMMUNITY`, `GOT`, `CYPHER`, `NAIVE`, and `SIMULATION`. +- **Streaming responses**: Server-Sent Events (SSE) with real-time reasoning steps surfaced in the UI +- **Multi-turn conversations**: Persistent conversation threads stored in Neo4j, per-user +- **Document-scoped queries**: Filter retrieval to a specific document via `document_id` +- **Graph of Thoughts (GoT)**: Advanced multi-hop reasoning mode exploring the graph neighborhood +- **HippoRAG**: Personalized PageRank (PPR) fallback strategies mapped to entity seeding +- **Global Community Search**: Vector similarity search over pre-computed hierarchical Leiden reports +- **LLM-as-a-Judge (inline)**: Optional per-response quality scoring with hallucination risk, grounded/ungrounded claims, and confidence reasoning displayed in chat +- **Confidence display**: Confidence score, hallucination risk, and judge reasoning shown directly in the chat bubble + +### 📊 RAGAS Evaluation & Quality Dashboard + +- **`POST /api/eval/score`**: Run RAGAS-style evaluation on any Q&A pair (faithfulness, relevancy, context precision, hallucination detection) +- **`GET /api/eval/dashboard`**: Aggregate evaluation history — avg scores, hallucination rate, trend timeline +- Results persisted in Neo4j for longitudinal quality tracking + +### 🗺️ Graph Intelligence + +- **D3 force-directed visualization**: Interactive knowledge graph with zoom, pan, node selection, and a details modal +- **Graph Export**: Export full or document-scoped graph as JSON, Cypher, or GraphML +- **Community Detection**: Hierarchical Leiden community clustering with `POST /api/graph/communities/assign` +- **Community listing**: `GET /api/graph/communities` — top communities by entity count +- **Temporal Queries**: `GET /api/entities/{entity_name}/at-time` — retrieve entity relationships at a historical point in time +- **Semantic Entity Deduplication**: Multi-stage entity resolution with configurable similarity thresholds (`POST /api/entities/deduplicate`) +- **Entity Enrichment**: LLM-synthesized profile summaries for every entity, stored as `e.summary` (`POST /api/entities/enrich`) +- **Entity Chat (scoped)**: `POST /api/entities/{entity_name}/chat` — multi-turn conversation scoped entirely to a single entity's graph neighborhood +- **Graph Memory Updater**: Push raw text directly into the live knowledge graph without re-ingesting a document (`POST /api/graph/update`) + +### 📝 Analytical Report Agent (ReACT) + +- **`POST /api/report`**: ReACT multi-step report agent using InsightForge / PanoramaSearch / QuickSearch tools +- Decomposes topic into sub-questions → retrieves graph data → synthesizes sections → compiles structured markdown report +- Exposed in the **Insights** view (copy/download report as Markdown) + +### 🎭 Simulation & Persona Engine + +- **Persona generation**: Celery task that generates personas from graph entities (`POST /api/v1/simulation/generate_personas`) +- **Simulation ticks**: Background tick loop (`POST /api/v1/simulation/tick`) +- **Live persona interview**: `POST /api/v1/simulation/interview` — roleplay chat with any graph entity injecting their Neo4j memory as system context +- **SimulationRunView**: Dedicated UI view for managing and interacting with simulation personas + +### 🛡️ Admin Dashboard + +- **System statistics**: Node count, relationship count, LLM provider, environment +- **User management**: List users, update scopes/roles (RBAC) +- **Document vault**: View and delete all ingested documents +- **Graph CRUD**: Search, inspect, and delete graph nodes from the admin panel +- **Ontology governance**: Review and approve/reject pending ontology proposals +- **Celery task monitor**: View active and reserved tasks from the admin panel +- **Self-demotion guard**: Admins cannot demote their own account +- **Re-ingest button**: Re-queue any stored document from the document vault +- **User activity metrics**: Per-user conversation count, message count, last active timestamp + +### 🔐 Authentication & Security + +- **JWT authentication**: Token-based auth with configurable expiry +- **RBAC scopes**: `read`, `write`, `admin` scopes enforced per endpoint +- **User registration**: `POST /api/auth/register` +- **Pydantic validation**: All API inputs validated at the model layer +- **Cypher injection prevention**: Schema validation and query whitelisting +- **File upload limits**: File size and MIME type enforcement + +### 🌐 Frontend (React/TypeScript) + +Seven fully implemented views accessible from the `CORTEX` top navigation bar: + +| Route | View | Description | +|---|---|---| +| `/` | **Home** | Animated stats dashboard — documents, entities, relationships, graph health | +| `/process` | **Process** | Upload files or scrape/crawl URLs; view ingestion queue and document list | +| `/ontology` | **Ontology** | View/edit the live ontology schema; run LLM refinement; inspect entity/relationship stats per doc | +| `/interact` | **Interact** | Streaming AI chat with reasoning steps, confidence, hallucination risk; conversation history | +| `/simulate` | **Simulate** | Simulation persona management and live interview interface | +| `/insights` | **Insights** | Topic-driven analytical report generation with copy/download | +| `/admin` | **Admin** _(admin-only)_ | Full admin panel for users, docs, tasks, ontology governance | + +### 🔭 Observability + +- **OpenTelemetry**: Distributed tracing (silenced from console; configured for export) +- **Health check**: `GET /api/system/health` — Neo4j, Redis, Celery worker status +- **System stats**: `GET /api/system/stats` — document, entity, relationship, chunk counts +- **User stats**: `GET /api/system/my-stats` — per-user conversation and message activity + +--- + +## 🏗️ Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ React Frontend (CORTEX) │ +│ Home │ Process │ Ontology │ Interact │ Simulate │ Insights │ Admin │ +└─────────────────────────────┬───────────────────────────────────────────────┘ + │ HTTP / SSE +┌─────────────────────────────▼───────────────────────────────────────────────┐ +│ FastAPI Gateway (port 8000) │ +│ JWT Auth · RBAC Scopes · CORS · OpenTelemetry │ +└──────┬──────────────────────┬──────────────────────┬────────────────────────┘ + │ │ │ +┌──────▼──────┐ ┌───────────▼──────────┐ ┌───────▼────────────────────┐ +│ Ingestion │ │ ReACT Agent System │ │ Report Agent (ReACT) │ +│ Pipeline │ │ - Vector Search │ │ - InsightForge │ +│ - Parser │ │ - Graph Traversal │ │ - PanoramaSearch │ +│ - Ontology │ │ - Cypher Gen (GoT) │ │ - QuickSearch │ +│ - Extractor│ │ - Community Search │ │ - Markdown output │ +│ - Web │ │ - Temporal Queries │ └────────────────────────────┘ +│ Crawler │ │ - LLM-as-a-Judge │ +└──────┬──────┘ └─────────┬────────────┘ + │ │ +┌──────▼────────────────────▼──────────────────┐ +│ Neo4j 5.x Database │ +│ Entities · Chunks · Relationships · │ +│ Vector Index · Conversations · │ +│ EvalResults · DriftReports · Users │ +└───────────────────────────────────────────────┘ + │ +┌──────▼──────────────────────┐ +│ Celery Workers (Redis) │ +│ - Async document ingestion │ +│ - Persona generation │ +│ - Simulation ticks │ +└─────────────────────────────┘ +``` + +--- + +## 📦 Project Structure + +``` +graph-RAG/ +├── src/graph_rag_service/ +│ ├── api/ +│ │ ├── server.py # Main FastAPI app + all API routes (1900 lines) +│ │ ├── auth.py # JWT auth + RBAC helpers +│ │ ├── admin.py # Admin sub-router +│ │ ├── simulation.py # Simulation / persona interview router +│ │ └── models.py # All Pydantic request/response models +│ ├── core/ +│ │ ├── abstractions.py # Abstract base classes (GraphStore, VectorStore, LLMProvider) +│ │ ├── models.py # Domain data models +│ │ ├── neo4j_store.py # Full Neo4j implementation (graph + vector) +│ │ ├── llm_factory.py # Multi-LLM provider factory + UnifiedLLMProvider +│ │ ├── entity_resolver.py # Semantic entity deduplication +│ │ └── storage.py # File storage abstraction +│ ├── ingestion/ +│ │ ├── pipeline.py # End-to-end ingestion orchestrator +│ │ ├── document_processor.py # Multi-format document parsing +│ │ ├── ontology_generator.py # LLM ontology generation + refinement +│ │ ├── extractor.py # Entity + relationship extraction +│ │ ├── web_crawler.py # Playwright-based deep web crawler (Crawl4AI) +│ │ └── persona_generator.py # Simulation persona generation +│ ├── retrieval/ +│ │ ├── agent.py # LangGraph ReACT retrieval agent +│ │ ├── tools.py # Retrieval tools + RAGEvaluator (RAGAS) +│ │ └── report_agent.py # ReACT analytical report agent +│ ├── services/ +│ │ ├── graph_memory_updater.py # Push raw text → live graph +│ │ ├── entity_enricher.py # LLM entity profile summaries +│ │ └── ontology_drift_detector.py # Automated schema drift detection +│ ├── workers/ +│ │ └── celery_worker.py # Celery app + ingest_document_task +│ ├── observability/ +│ │ └── tracing.py # OpenTelemetry setup (console suppressed) +│ ├── config.py # Pydantic settings (all env vars) +│ └── main.py # Uvicorn entry point +├── frontend-react/ +│ └── src/ +│ ├── views/ +│ │ ├── Home.tsx # Animated stats dashboard +│ │ ├── Process.tsx # Document upload + URL scrape/crawl +│ │ ├── Ontology.tsx # Schema editor + stats +│ │ ├── InteractionView.tsx # Streaming chat + conversation history +│ │ ├── SimulationRunView.tsx # Persona simulation UI +│ │ ├── InsightsView.tsx # Report generation + copy/download +│ │ ├── AdminDashboard.tsx # Full admin panel +│ │ └── Login.tsx # Login page +│ ├── components/ +│ │ └── GraphCanvas.tsx # D3 force-directed graph + node modal +│ ├── context/ +│ │ └── AuthContext.tsx # JWT auth context + hooks +│ └── App.tsx # Router + top-nav (CORTEX branding) +├── tests/ # Test suite +├── data/uploads/ # Uploaded documents (local storage) +├── .env.example # All configurable environment variables +├── pyproject.toml # Python project + uv dependencies +├── package.json # Unified start scripts (npm run rag) +├── ARCHITECTURE.md # Detailed architecture design doc +└── QUICKSTART.md # 5-minute quick start guide +``` + +--- + +## ⚡ Quick Start + +### Prerequisites + +- Python 3.12+ +- Node.js 18+ +- Neo4j 5.x (running) with **APOC** and **Graph Data Science (GDS)** plugins installed +- Redis (running) +- Ollama *(optional, for local LLMs)* + +### 1. Clone & Install + +```bash +git clone +cd graph-RAG + +# Installs Python deps (uv), frontend (npm), and Playwright Chromium +npm install +``` + +### 2. Configure Environment + +```bash +cp .env.example .env +# Fill in NEO4J_URI, NEO4J_PASSWORD, and your LLM API keys +``` + +### 3. Start Neo4j (Requires GDS Plugin) + +```bash +docker run -d --name neo4j \ + -p 7474:7474 -p 7687:7687 \ + -e NEO4J_AUTH=neo4j/password \ + -e NEO4J_PLUGINS='["graph-data-science", "apoc"]' \ + neo4j:5.18.0 +``` + +### 4. Start Redis + +```bash +docker run -d --name redis -p 6379:6379 redis:alpine +``` + +### Hugging Face Spaces / All-in-One Docker +You can easily deploy CORTEX to Hugging Face Spaces or as a standalone container. +Our `Dockerfile` automatically installs Python 3.12, Redis, Neo4j, and the GDS plugin to run the entire platform on a single port (`7860`). + +### 5. Launch Everything + +```bash +npm run rag +``` + +This starts three color-coded processes concurrently: + +| Process | URL | +|---|---| +| **API Server** | `http://localhost:8000` | +| **API Docs** | `http://localhost:8000/docs` | +| **React Frontend** | `http://localhost:5173` | + +> Default credentials: `admin` / `admin` + +--- + +## 🔑 Environment Variables + +Copy `.env.example` to `.env` and configure: + +```env +# Neo4j +NEO4J_URI=bolt://localhost:7687 +NEO4J_USER=neo4j +NEO4J_PASSWORD=password + +# Redis +REDIS_HOST=localhost +REDIS_PORT=6379 + +# LLM Provider (openai | anthropic | gemini | ollama) +DEFAULT_LLM_PROVIDER=gemini +GOOGLE_API_KEY=your-key-here + +# Optional: OpenAI / Anthropic +OPENAI_API_KEY=sk-... +ANTHROPIC_API_KEY=sk-ant-... + +# Optional: Ollama (local) +OLLAMA_BASE_URL=http://localhost:11434 +OLLAMA_MODEL=deepseek-r1:7b +OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +# Feature flags +ENABLE_LLM_JUDGE=true + +# Security +SECRET_KEY=change-this-in-production +ACCESS_TOKEN_EXPIRE_MINUTES=1440 +``` + +--- + +## 🌐 API Reference + +### Authentication +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/auth/register` | Register new user | +| `POST` | `/api/auth/login` | Login → JWT token | +| `GET` | `/api/auth/me` | Get current user info | + +### Documents +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/documents/upload` | Upload file (PDF, DOCX, TXT, MD, CSV, XLSX, PPTX, JSON) | +| `POST` | `/api/documents/scrape` | Scrape single URL → ingest | +| `POST` | `/api/documents/crawl` | Deep multi-page Playwright crawl → ingest *(API Only)* | +| `GET` | `/api/documents` | List all ingested documents | +| `DELETE` | `/api/documents/{id}` | Delete document + graph chunks | +| `GET` | `/api/documents/{id}/download` | Download source file | +| `GET` | `/api/documents/{id}/preview` | Preview text content | +| `GET` | `/api/documents/status/{task_id}` | Ingestion task status | + +### Query & Chat +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/query` | Agentic query (streaming or JSON); supports `document_id`, `use_got` | +| `GET` | `/api/conversations` | List conversation threads | +| `GET` | `/api/conversations/{id}` | Get conversation + messages | +| `DELETE` | `/api/conversations/{id}` | Delete conversation | + +### Ontology +| Method | Endpoint | Description | +|---|---|---| +| `GET` | `/api/ontology` | Get current ontology | +| `PUT` | `/api/ontology` | Update ontology (admin) | +| `POST` | `/api/ontology/refine` | LLM-powered ontology refinement | +| `GET` | `/api/ontology/stats` | Entity/relationship counts (optional doc filter) | +| `POST` | `/api/ontology/drift/detect` | Trigger drift detection | +| `GET` | `/api/ontology/drift` | List drift reports | +| `POST` | `/api/ontology/drift/{id}/approve` | Approve drift → merge into ontology | +| `POST` | `/api/ontology/drift/{id}/reject` | Reject drift report | + +### Graph +| Method | Endpoint | Description | +|---|---|---| +| `GET` | `/api/graph/visualization` | Graph nodes + edges for D3 rendering | +| `GET` | `/api/graph/export` | Export graph (json \| cypher \| graphml) | +| `POST` | `/api/graph/update` | Push raw text → merge into live graph | +| `POST` | `/api/graph/communities/assign` | Run Hierarchical Leiden clustering & generate reports | +| `GET` | `/api/graph/communities` | List top communities | + +### Entities +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/entities/deduplicate` | Semantic entity resolution + merge | +| `POST` | `/api/entities/enrich` | Generate LLM summaries for all entities | +| `GET` | `/api/entities/{name}/summary` | Get enriched entity profile | +| `POST` | `/api/entities/{name}/chat` | Multi-turn entity-scoped chat | +| `GET` | `/api/entities/{name}/at-time` | Temporal query (ISO 8601 date) | + +### Reports & Evaluation +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/report` | Generate ReACT analytical report (markdown) | +| `POST` | `/api/eval/score` | RAGAS evaluation of a Q&A pair | +| `GET` | `/api/eval/dashboard` | Evaluation history dashboard | + +### Simulation +| Method | Endpoint | Description | +|---|---|---| +| `POST` | `/api/v1/simulation/interview` | Live persona interview (in-character LLM) | +| `GET` | `/api/v1/simulation/report` | Sandbox analytical report *(API Only)* | +| `POST` | `/api/v1/simulation/generate_personas` | Queue persona generation task *(API Only)* | +| `POST` | `/api/v1/simulation/tick` | Advance simulation tick *(API Only)* | + +### System & Admin +| Method | Endpoint | Description | +|---|---|---| +| `GET` | `/api/system/health` | Neo4j + Redis + Celery health | +| `GET` | `/api/system/stats` | Document, entity, relationship counts | +| `GET` | `/api/system/my-stats` | Current user's activity stats | +| `GET` | `/api/system/formats` | Supported ingestion file formats | +| `GET` | `/api/admin/stats` | Admin-only system stats | +| `GET` | `/api/admin/users` | List all users | +| `PUT` | `/api/admin/users/{username}/role` | Update user scopes | +| `GET` | `/api/admin/tasks` | View Celery tasks | +| `GET` | `/api/admin/documents` | Admin document vault | +| `POST` | `/api/admin/documents/{id}/reingest` | Re-queue document for ingestion | +| `GET` | `/api/admin/graph/nodes` | Search graph nodes | +| `DELETE` | `/api/admin/graph/nodes/{id}` | Delete a graph node | + +--- + +## 🧪 Testing & Benchmarking + +```bash +# Run tests +uv run pytest + +# With coverage +uv run pytest --cov=src/graph_rag_service + +# Run SOTA Benchmarks against Hugging Face datasets (e.g. HotpotQA, MuSiQue) +uv run python benchmarks/run_benchmark.py +``` + +--- + +## 🚀 Production Deployment + +| Process | Command | +|---|---| +| **API Server** | `uv run python main.py` | +| **Celery Worker** | `uv run celery -A src.graph_rag_service.workers.celery_worker worker --loglevel=info --concurrency=4 --pool=threads` | +| **React Build** | `cd frontend-react && npm run build` | + +The built React assets can be served directly by FastAPI (static file mount), or deployed to a CDN separately. Neo4j and Redis can be run via Docker, managed cloud services (AuraDB, Redis Cloud), or self-hosted. + +--- + +## 📄 Additional Documentation + +- **[ARCHITECTURE.md](./ARCHITECTURE.md)** — Deep dive into the system design, data flow, and component interactions +- **[QUICKSTART.md](./QUICKSTART.md)** — 5-minute environment setup guide +- **`/docs`** — Interactive Swagger UI (auto-generated from FastAPI) + +--- + +**Project Status**: Production-grade MVP · Actively developed +**License**: Proprietary — all rights reserved diff --git a/benchmarks/run_benchmark.py b/benchmarks/run_benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..2f577cd95a739403ca3946223d551e78b413d65b --- /dev/null +++ b/benchmarks/run_benchmark.py @@ -0,0 +1,299 @@ +import asyncio +import json +import time +import httpx +from typing import List, Dict, Any +from pydantic import BaseModel +import re +import string + +def normalize_answer(s): + """Lower text and remove punctuation, articles and extra whitespace.""" + def remove_articles(text): + return re.sub(r'\b(a|an|the)\b', ' ', text) + def white_space_fix(text): + return ' '.join(text.split()) + def remove_punc(text): + exclude = set(string.punctuation) + return ''.join(ch for ch in text if ch not in exclude) + def lower(text): + return text.lower() + return white_space_fix(remove_articles(remove_punc(lower(s)))) + +def exact_match(prediction, ground_truth): + return normalize_answer(prediction) == normalize_answer(ground_truth) + +def token_f1(prediction, ground_truth): + prediction_tokens = normalize_answer(prediction).split() + ground_truth_tokens = normalize_answer(ground_truth).split() + common = set(prediction_tokens) & set(ground_truth_tokens) + num_same = len(common) + if num_same == 0: + return 0.0 + precision = 1.0 * num_same / len(prediction_tokens) + recall = 1.0 * num_same / len(ground_truth_tokens) + f1 = (2 * precision * recall) / (precision + recall) + return f1 + +# Mock datasets for benchmarking fallback +HOTPOT_QA_SAMPLE = [ + { + "question": "What is the capital of the country where the city of Lyon is located?", + "ground_truth": "Paris", + "context": "Lyon is a city in France. The capital of France is Paris.", + "type": "multi-hop" + }, + { + "question": "Which company acquired the startup that developed the Siri virtual assistant?", + "ground_truth": "Apple", + "context": "Siri was originally developed by Siri Inc. Apple acquired Siri Inc. in 2010.", + "type": "multi-hop" + } +] + +class BenchmarkConfig(BaseModel): + base_url: str = "http://localhost:7860" + modes: List[str] = ["naive", "hybrid", "hippo", "global_community"] + dataset: str = "hotpot_qa" + num_samples: int = 10 + +def load_hf_dataset(config: BenchmarkConfig) -> List[Dict[str, str]]: + try: + from datasets import load_dataset # type: ignore + print(f"Loading {config.dataset} from Hugging Face datasets...") + + if config.dataset == "hotpot_qa": + ds = load_dataset("hotpot_qa", "distractor", split="validation", streaming=True) + elif config.dataset == "musique": + ds = load_dataset("bdsaglam/musique", split="validation", streaming=True) + else: + ds = load_dataset(config.dataset, split="validation", streaming=True) + + samples = [] + for item in ds: + if len(samples) >= config.num_samples: + break + + # Extract context text + context_text = "" + if "context" in item: + # HotpotQA format: lists of titles and sentences + if isinstance(item["context"], dict) and "sentences" in item["context"]: + for sentences in item["context"]["sentences"]: + context_text += " ".join(sentences) + " " + else: + context_text = str(item["context"]) + + samples.append({ + "question": item.get("question", ""), + "ground_truth": item.get("answer", ""), + "context": context_text, + "type": item.get("level", "unknown") + }) + return samples + except ImportError: + print("HF 'datasets' library not installed. Falling back to mock dataset.") + return HOTPOT_QA_SAMPLE + except Exception as e: + print(f"Failed to load dataset from HF: {e}. Falling back to mock dataset.") + return HOTPOT_QA_SAMPLE + +async def create_benchmark_tenant(client: httpx.AsyncClient, config: BenchmarkConfig): + """Returns (token, tenant_id) for the isolated benchmark tenant.""" + timestamp = int(time.time()) + username = f"benchmark_user_{timestamp}" + tenant_id = f"benchmark_run_{timestamp}" + password = "password123" + + register_url = f"{config.base_url}/api/auth/register" + payload = { + "username": username, + "password": password, + "email": f"{username}@example.com", + "full_name": "Benchmark User", + "scopes": ["read", "write"], + "tenant_id": tenant_id + } + + try: + res = await client.post(register_url, json=payload, timeout=10.0) + res.raise_for_status() + + login_url = f"{config.base_url}/api/auth/login" + login_payload = {"username": username, "password": password} + login_res = await client.post(login_url, json=login_payload, timeout=10.0) + login_res.raise_for_status() + token = login_res.json()["access_token"] + print(f" Created isolated tenant: {tenant_id}") + return token, tenant_id + except Exception as e: + print(f" Failed to create isolated tenant: {e}. Falling back to default admin.") + token = await authenticate(client, config) + return token, "admin" + +async def authenticate(client: httpx.AsyncClient, config: BenchmarkConfig) -> str: + login_url = f"{config.base_url}/api/auth/login" + try: + response = await client.post(login_url, json={"username": "admin", "password": "admin"}, timeout=10.0) + response.raise_for_status() + return response.json().get("access_token", "") + except Exception as e: + print(f"Failed to authenticate with backend: {e}. Some endpoints may be inaccessible.") + return "test-token" + +async def ingest_context(client: httpx.AsyncClient, config: BenchmarkConfig, token: str, q: Dict[str, str]): + if not q.get("context"): + return + + update_url = f"{config.base_url}/api/graph/update" + headers = {"Authorization": f"Bearer {token}"} + payload = { + "text": q["context"], + "source_label": "benchmark_ingest" + } + + try: + response = await client.post(update_url, json=payload, headers=headers, timeout=30.0) + response.raise_for_status() + except Exception as e: + print(f" [Warning] Failed to ingest context: {e}") + +async def evaluate_question(client: httpx.AsyncClient, config: BenchmarkConfig, token: str, mode: str, q: Dict[str, str]) -> Dict[str, Any]: + query_url = f"{config.base_url}/api/query" + payload = { + "query": q["question"], + "top_k": 5, + "mode": mode, + "streaming": False + } + + start_time = time.time() + try: + headers = {"Authorization": f"Bearer {token}"} + + response = await client.post(query_url, json=payload, headers=headers, timeout=60.0) + response.raise_for_status() + data = response.json() + + duration = time.time() - start_time + answer = data.get("answer", "") + + em = exact_match(answer, q["ground_truth"]) + f1 = token_f1(answer, q["ground_truth"]) + is_correct = em or f1 > 0.6 # Use relaxed F1 threshold for general correctness flag + + return { + "question": q["question"], + "mode": mode, + "duration": duration, + "is_correct": is_correct, + "exact_match": em, + "f1_score": f1, + "generated_answer": answer, + "confidence": data.get("confidence", 0.0), + "sources_count": len(data.get("sources", [])) + } + except Exception as e: + return { + "question": q["question"], + "mode": mode, + "duration": time.time() - start_time, + "is_correct": False, + "error": str(e) + } + +async def build_communities(client: httpx.AsyncClient, config: BenchmarkConfig, token: str): + """Build community index once before evaluating global_community mode.""" + communities_url = f"{config.base_url}/api/graph/communities/assign" + headers = {"Authorization": f"Bearer {token}"} + try: + res = await client.post(communities_url, headers=headers, timeout=120.0) + res.raise_for_status() + print(f" Community index built: {res.json()}") + except Exception as e: + print(f" [Warning] Community indexing failed: {e}") + +async def cleanup_benchmark_tenant(client: httpx.AsyncClient, config: BenchmarkConfig, token: str, tenant_id: str): + """Delete all graph data for the benchmark tenant.""" + cleanup_url = f"{config.base_url}/api/graph/purge-tenant" + headers = {"Authorization": f"Bearer {token}"} + try: + res = await client.request( + "DELETE", + cleanup_url, + headers=headers, + json={"tenant_id": tenant_id}, + timeout=30.0 + ) + res.raise_for_status() + print(f" Benchmark tenant {tenant_id} cleaned up.") + except Exception as e: + print(f" [Warning] Cleanup failed for tenant {tenant_id}: {e}") + +async def run_benchmark(): + config = BenchmarkConfig() + dataset = load_hf_dataset(config) + + print(f"Starting benchmark on {config.dataset} with {len(dataset)} questions...") + print(f"Modes to test: {config.modes}") + + results = [] + + async with httpx.AsyncClient() as client: + # Authenticate first + # Create isolated tenant for benchmark + print("\nCreating isolated benchmark tenant...") + token, benchmark_tenant_id = await create_benchmark_tenant(client, config) + + has_community_mode = any(m in config.modes for m in ["global_community", "hippo"]) + + # Ingest all contexts first before building communities or evaluating + print("\nIngesting all context documents...") + for i, q in enumerate(dataset): + print(f" Ingesting context {i+1}/{len(dataset)}...") + await ingest_context(client, config, token, q) + + # Build community index once if needed + if has_community_mode: + print("\nBuilding community index (required for global_community mode)...") + await asyncio.sleep(2) # Allow Neo4j to settle + await build_communities(client, config, token) + + for i, q in enumerate(dataset): + print(f"\nEvaluating Question {i+1}/{len(dataset)}: {q['question']}") + # Context already ingested above + for mode in config.modes: + print(f" Running mode: {mode}...") + res = await evaluate_question(client, config, token, mode, q) + results.append(res) + status = "PASS" if res.get("is_correct") else "FAIL" + f1 = res.get("f1_score", 0.0) + print(f" [{status}] F1: {f1:.2f} | Time: {res['duration']:.2f}s | Sources: {res.get('sources_count', 0)}") + + summary = {} + for r in results: + m = r["mode"] + if m not in summary: + summary[m] = {"correct": 0, "total": 0, "time": 0.0} + + summary[m]["total"] += 1 + if r.get("is_correct"): + summary[m]["correct"] += 1 + summary[m]["time"] += r["duration"] + summary[m]["f1"] = summary[m].get("f1", 0.0) + r.get("f1_score", 0.0) + + print("\n=== BENCHMARK RESULTS ===") + for m, stats in summary.items(): + accuracy = (stats["correct"] / stats["total"]) * 100 if stats["total"] > 0 else 0 + avg_time = stats["time"] / stats["total"] if stats["total"] > 0 else 0 + avg_f1 = stats["f1"] / stats["total"] if stats["total"] > 0 else 0 + print(f"Mode: {m:<15} | Accuracy: {accuracy:>5.1f}% | Avg F1: {avg_f1:.3f} | Avg Time: {avg_time:>5.2f}s") + + # Cleanup: remove all benchmark tenant data using admin token (purge-tenant requires admin scope) + if benchmark_tenant_id != "admin": + async with httpx.AsyncClient() as cleanup_client: + admin_token = await authenticate(cleanup_client, BenchmarkConfig()) + await cleanup_benchmark_tenant(cleanup_client, config, admin_token, benchmark_tenant_id) + +if __name__ == "__main__": + asyncio.run(run_benchmark()) diff --git a/data/uploads/.gitkeep b/data/uploads/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..fdf46cb225600706e735e36f4374da3aadf8ddac --- /dev/null +++ b/data/uploads/.gitkeep @@ -0,0 +1 @@ +# This file ensures the data/uploads directory is created in git diff --git a/fix_datetime.py b/fix_datetime.py new file mode 100644 index 0000000000000000000000000000000000000000..e3ce831d8c758f1bc539b8941ec3aec45f943919 --- /dev/null +++ b/fix_datetime.py @@ -0,0 +1,21 @@ +import os + +target_dir = r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service" + +for root, dirs, files in os.walk(target_dir): + for f in files: + if f.endswith(".py"): + p = os.path.join(root, f) + with open(p, "r", encoding="utf-8") as file: + content = file.read() + + if "datetime.utcnow()" in content: + content = content.replace("datetime.utcnow()", "datetime.now(timezone.utc).replace(tzinfo=None)") + if "from datetime import datetime" in content and "timezone" not in content: + content = content.replace("from datetime import datetime", "from datetime import datetime, timezone") + elif "import datetime" in content and "timezone" not in content: + content = "from datetime import timezone\n" + content + + with open(p, "w", encoding="utf-8") as file: + file.write(content) + print(f"Updated {p}") diff --git a/fix_default_factory.py b/fix_default_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..9d6ad417f6499ad280562cf30017ed67aecd328e --- /dev/null +++ b/fix_default_factory.py @@ -0,0 +1,12 @@ +import os +import glob + +for f in glob.glob(r'D:\Desktop_March_26\LYZR\graph-RAG\src\**\*.py', recursive=True): + with open(f, 'r', encoding='utf-8') as file: + content = file.read() + + if 'default_factory=datetime.utcnow' in content: + new_content = content.replace('default_factory=datetime.utcnow', 'default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)') + with open(f, 'w', encoding='utf-8') as file: + file.write(new_content) + print(f"Fixed {f}") diff --git a/fix_print_statements.py b/fix_print_statements.py new file mode 100644 index 0000000000000000000000000000000000000000..456aab29716f8a525b18937944317aa2f4662132 --- /dev/null +++ b/fix_print_statements.py @@ -0,0 +1,21 @@ +import os +import glob +import re + +for f in glob.glob(r'D:\Desktop_March_26\LYZR\graph-RAG\src\**\*.py', recursive=True): + with open(f, 'r', encoding='utf-8') as file: + content = file.read() + + if 'print(' in content: + # Add import logging and logger if not exists + if 'import logging' not in content: + content = "import logging\nlogger = logging.getLogger(__name__)\n" + content + elif 'logger = ' not in content: + content = content.replace('import logging\n', 'import logging\nlogger = logging.getLogger(__name__)\n', 1) + + # Replace print( with logger.info( + content = re.sub(r'\bprint\(', 'logger.info(', content) + + with open(f, 'w', encoding='utf-8') as file: + file.write(content) + print(f"Fixed {f}") diff --git a/fix_timezone_imports.py b/fix_timezone_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..45d0188fcab9633cf36f70b9d5f6f27027d9339b --- /dev/null +++ b/fix_timezone_imports.py @@ -0,0 +1,33 @@ +import os + +files = [ + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\api\auth.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\api\routers\ontology.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\api\routers\system.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\ingestion\document_processor.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\ingestion\ontology_generator.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\retrieval\report_agent.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\services\graph_memory_updater.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\services\ontology_drift_detector.py", + r"D:\Desktop_March_26\LYZR\graph-RAG\src\graph_rag_service\workers\simulation_runner.py" +] + +for f in files: + with open(f, 'r', encoding='utf-8') as file: + content = file.read() + + # Check if 'from datetime import datetime' exists + if "from datetime import datetime\n" in content: + content = content.replace("from datetime import datetime\n", "from datetime import datetime, timezone\n") + elif "from datetime import datetime," in content: + if "timezone" not in content: + content = content.replace("from datetime import datetime,", "from datetime import datetime, timezone,") + elif "import datetime\n" in content: + content = content.replace("import datetime\n", "import datetime\nfrom datetime import timezone\n") + else: + # Just put it at the top + content = "from datetime import timezone\n" + content + + with open(f, 'w', encoding='utf-8') as file: + file.write(content) + print(f"Fixed {f}") diff --git a/frontend-react/.gitignore b/frontend-react/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a547bf36d8d11a4f89c59c144f24795749086dd1 --- /dev/null +++ b/frontend-react/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend-react/README.md b/frontend-react/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7dbf7ebf3b2a3d84ad526bc47810d1d211331b8b --- /dev/null +++ b/frontend-react/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/frontend-react/eslint.config.js b/frontend-react/eslint.config.js new file mode 100644 index 0000000000000000000000000000000000000000..5e6b472f583e34a1cca751440d4f241495475723 --- /dev/null +++ b/frontend-react/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend-react/index.html b/frontend-react/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c270cc86c8c4129d902d988fc9cb7711cf4d59b7 --- /dev/null +++ b/frontend-react/index.html @@ -0,0 +1,13 @@ + + + + + + + frontend-react + + +
+ + + diff --git a/frontend-react/package-lock.json b/frontend-react/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..03c92b67b33c76b17fd59a5864e923fe0eb6e51b --- /dev/null +++ b/frontend-react/package-lock.json @@ -0,0 +1,6053 @@ +{ + "name": "frontend-react", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend-react", + "version": "0.0.0", + "dependencies": { + "@heroicons/react": "^2.2.0", + "@types/d3": "^7.4.3", + "d3": "^7.9.0", + "lucide-react": "^1.7.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-router-dom": "^7.13.2", + "remark-gfm": "^4.0.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@tailwindcss/vite": "^4.2.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "concurrently": "^9.2.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "openapi-typescript-codegen": "^0.30.0", + "postcss": "^8.5.10", + "tailwindcss": "^4.2.4", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.1.tgz", + "integrity": "sha512-HmdFw9CDYqM6B25pqGBpNeLCKvGPlIx1EbLrVL0zPvj50CJQUHyBNBw45Muk0kEIkogo1VZvOKHajdMuAzSxRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + }, + "peerDependencies": { + "@types/json-schema": "^7.0.15" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", + "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "tailwindcss": "4.2.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.57.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001790", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", + "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz", + "integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript-codegen": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.30.0.tgz", + "integrity": "sha512-NO24vrOYEEREkuEwtLemXiV0/3wUj1HvS+0UuAinVNWKJOyNlXTj5hehdW9Dyob4u5YGrRG9dc9TBZW7/UszGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^14.2.1", + "camelcase": "^6.3.0", + "commander": "^14.0.2", + "fs-extra": "^11.3.3", + "handlebars": "^4.7.8" + }, + "bin": { + "openapi": "bin/index.js" + } + }, + "node_modules/openapi-typescript-codegen/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-router": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", + "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz", + "integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/frontend-react/package.json b/frontend-react/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f7c0e3f7fdac425be9526779c2ec2da6708db40e --- /dev/null +++ b/frontend-react/package.json @@ -0,0 +1,46 @@ +{ + "name": "frontend-react", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "server": "cd ../ && .\\venv\\Scripts\\python.exe -m uvicorn src.graph_rag_service.api.server:app --reload", + "worker": "cd ../ && .\\venv\\Scripts\\celery.exe -A src.graph_rag_service.workers.celery_worker worker --loglevel=info --pool=solo", + "backend": "concurrently \"npm run server\" \"npm run worker\"" + }, + "dependencies": { + "@heroicons/react": "^2.2.0", + "@types/d3": "^7.4.3", + "d3": "^7.9.0", + "lucide-react": "^1.7.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-router-dom": "^7.13.2", + "remark-gfm": "^4.0.1" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@tailwindcss/vite": "^4.2.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "concurrently": "^9.2.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "openapi-typescript-codegen": "^0.30.0", + "postcss": "^8.5.10", + "tailwindcss": "^4.2.4", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } +} diff --git a/frontend-react/public/_redirects b/frontend-react/public/_redirects new file mode 100644 index 0000000000000000000000000000000000000000..50a463356b7d89bc7f17cfe56003eab71b56d8ad --- /dev/null +++ b/frontend-react/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/frontend-react/public/favicon.svg b/frontend-react/public/favicon.svg new file mode 100644 index 0000000000000000000000000000000000000000..6893eb13237060adc0c968a690149a49faa2d7d3 --- /dev/null +++ b/frontend-react/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend-react/public/icons.svg b/frontend-react/public/icons.svg new file mode 100644 index 0000000000000000000000000000000000000000..e9522193d9f796a9748e9ad8c952a5df73c87db9 --- /dev/null +++ b/frontend-react/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend-react/public/thumbnail.png b/frontend-react/public/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..e7710d434efabfad76c943ddb61aa42c70a56581 --- /dev/null +++ b/frontend-react/public/thumbnail.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c0fd3b4ca20ef442918b0cd3504825b0a966e94d681732c036298395d5a773a +size 545039 diff --git a/frontend-react/src/App.css b/frontend-react/src/App.css new file mode 100644 index 0000000000000000000000000000000000000000..456ff12b618716728a022ab654c8a837b9cd74e9 --- /dev/null +++ b/frontend-react/src/App.css @@ -0,0 +1 @@ +/* Empty file to override default vite styles */ diff --git a/frontend-react/src/App.tsx b/frontend-react/src/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..198dca67a098ea8a5fde2d96981d512f154882da --- /dev/null +++ b/frontend-react/src/App.tsx @@ -0,0 +1,179 @@ +import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate, NavLink } from 'react-router-dom'; +import { AuthProvider, useAuth } from './context/AuthContext'; + +import Login from './views/Login'; +import Home from './views/Home'; +import Process from './views/Process'; +import InteractionView from './views/InteractionView'; +import SimulationRunView from './views/SimulationRunView'; +import Ontology from './views/Ontology'; +import InsightsView from './views/InsightsView'; +import AdminDashboard from './views/AdminDashboard'; + +const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { + const { isAuthenticated } = useAuth(); + if (!isAuthenticated) return ; + return children; +}; + +const Navigation: React.FC = () => { + const { logout, user } = useAuth(); + return ( + + ); +}; + +const Layout = ({ children }: { children: React.ReactNode }) => ( + <> + + {children} + +); + +function App() { + return ( + + + + } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); +} + +export default App; diff --git a/frontend-react/src/assets/hero.png b/frontend-react/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..2d58a13c6c916ee1d261fc82517761fa3acf2c8d --- /dev/null +++ b/frontend-react/src/assets/hero.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72a860570eddf1dd9988f26c7106c67be286bc9f2fd3303c465ce87edb1ae6cd +size 44919 diff --git a/frontend-react/src/assets/react.svg b/frontend-react/src/assets/react.svg new file mode 100644 index 0000000000000000000000000000000000000000..6c87de9bb3358469122cc991d5cf578927246184 --- /dev/null +++ b/frontend-react/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend-react/src/assets/vite.svg b/frontend-react/src/assets/vite.svg new file mode 100644 index 0000000000000000000000000000000000000000..5101b674df391399da71c767aa5c976426c9dc7a --- /dev/null +++ b/frontend-react/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/frontend-react/src/components/GraphCanvas.tsx b/frontend-react/src/components/GraphCanvas.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a172f585f4f8761f53e0bd223cc97d8e2e6f594b --- /dev/null +++ b/frontend-react/src/components/GraphCanvas.tsx @@ -0,0 +1,570 @@ +import { useEffect, useRef, forwardRef, useImperativeHandle, useState } from 'react'; +import * as d3 from 'd3'; +import { X, Tag, FileText, Database } from 'lucide-react'; + +// 12-color categorical palette for node types +const TYPE_COLORS = [ + '#e63946', '#457b9d', '#2a9d8f', '#e9c46a', '#f4a261', + '#6a4c93', '#1982c4', '#8ac926', '#ff595e', '#6a994e', + '#bc4749', '#a8dadc' +]; + +export interface GraphOptions { + colorByType: boolean; + showLabels: boolean; + showEdgeLabels: boolean; + nodeRadius: number; + linkDistance: number; + chargeStrength: number; + showCurvedEdges: boolean; + nodeSizeByDegree: boolean; + centerGravity: number; // 0 = no gravity, 0.1 = default +} + +export const DEFAULT_OPTIONS: GraphOptions = { + colorByType: true, + showLabels: true, + showEdgeLabels: false, + nodeRadius: 16, + linkDistance: 120, + chargeStrength: -300, + showCurvedEdges: false, + nodeSizeByDegree: false, + centerGravity: 0.05, +}; + +interface GraphCanvasProps { + data: { nodes: any[]; edges: any[] }; + onNodeUpdate?: (nodeId: string, newName: string) => void; + options?: GraphOptions; + highlightNodeIds?: Set; // nodes to highlight (from search) +} + +export interface GraphCanvasHandle { + exportPNG: () => void; + exportSVG: () => void; + fitView: () => void; + highlightNode: (id: string) => void; +} + +const GraphCanvas = forwardRef( + ({ data, onNodeUpdate, options = DEFAULT_OPTIONS, highlightNodeIds }, ref) => { + const [activeNode, setActiveNode] = useState(null); + const containerRef = useRef(null); + const svgRef = useRef(null); + const zoomRef = useRef | null>(null); + const gRef = useRef | null>(null); + const simulationRef = useRef | null>(null); + const typeColorMap = useRef>(new Map()); + + // ── Imperative API ───────────────────────────────────────────────────── + useImperativeHandle(ref, () => ({ + exportPNG() { + if (!svgRef.current) return; + const svgEl = svgRef.current; + const serializer = new XMLSerializer(); + const svgStr = serializer.serializeToString(svgEl); + const canvas = document.createElement('canvas'); + canvas.width = svgEl.clientWidth * 2; + canvas.height = svgEl.clientHeight * 2; + const ctx = canvas.getContext('2d')!; + const img = new Image(); + const blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' }); + const url = URL.createObjectURL(blob); + img.onload = () => { + ctx.fillStyle = '#fff'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.scale(2, 2); + ctx.drawImage(img, 0, 0); + URL.revokeObjectURL(url); + const a = document.createElement('a'); + a.download = 'graph.png'; + a.href = canvas.toDataURL('image/png'); + a.click(); + }; + img.src = url; + }, + exportSVG() { + if (!svgRef.current) return; + const serializer = new XMLSerializer(); + const svgStr = serializer.serializeToString(svgRef.current); + const blob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.download = 'graph.svg'; + a.href = url; + a.click(); + URL.revokeObjectURL(url); + }, + fitView() { + if (!svgRef.current || !zoomRef.current) return; + d3.select(svgRef.current).transition().duration(600).call( + zoomRef.current.transform, d3.zoomIdentity + ); + }, + highlightNode(id: string) { + if (!svgRef.current || !zoomRef.current) return; + const node = simulationRef.current?.nodes().find((n: any) => n.id === id); + if (!node || node.x === undefined) return; + const svg = d3.select(svgRef.current); + const w = svgRef.current.clientWidth; + const h = svgRef.current.clientHeight; + const t = d3.zoomIdentity.translate(w / 2 - node.x, h / 2 - node.y); + svg.transition().duration(700).call(zoomRef.current.transform, t); + } + })); + + // ── Main D3 render effect ────────────────────────────────────────────── + useEffect(() => { + if (!data.nodes.length || !containerRef.current || !svgRef.current) return; + + const width = containerRef.current.clientWidth; + const height = containerRef.current.clientHeight; + + // Build type→color map (stable) + const types = [...new Set(data.nodes.map(n => n.type || 'Unknown'))]; + types.forEach((t, i) => { + if (!typeColorMap.current.has(t)) { + typeColorMap.current.set(t, TYPE_COLORS[i % TYPE_COLORS.length]); + } + }); + + const svg = d3.select(svgRef.current); + svg.selectAll('*').remove(); + + const nodes: any[] = data.nodes.map(d => ({ ...d })); + const nodeIds = new Set(nodes.map(n => n.id)); + const links: any[] = data.edges + .filter(d => nodeIds.has(d.source) && nodeIds.has(d.target)) + .map(d => ({ ...d })); + + // Degree map for node-size-by-degree + const degreeMap = new Map(); + nodes.forEach(n => degreeMap.set(n.id, 0)); + links.forEach(l => { + const sid = typeof l.source === 'object' ? l.source.id : l.source; + const tid = typeof l.target === 'object' ? l.target.id : l.target; + degreeMap.set(sid, (degreeMap.get(sid) || 0) + 1); + degreeMap.set(tid, (degreeMap.get(tid) || 0) + 1); + }); + const maxDegree = Math.max(1, ...degreeMap.values()); + + const nodeR = (d: any) => { + if (!options.nodeSizeByDegree) return options.nodeRadius; + const deg = degreeMap.get(d.id) || 0; + return Math.max(8, options.nodeRadius * (0.5 + 1.0 * (deg / maxDegree))); + }; + + // ── Defs: arrowhead markers ────────────────────────────────────────── + const defs = svg.append('defs'); + if (options.colorByType) { + types.forEach(t => { + const color = typeColorMap.current.get(t) || '#000'; + defs.append('marker') + .attr('id', `arrow-${t.replace(/\s+/g, '_')}`) + .attr('viewBox', '-0 -5 10 10').attr('refX', options.nodeRadius + 10) + .attr('refY', 0).attr('orient', 'auto') + .attr('markerWidth', 6).attr('markerHeight', 6) + .append('path').attr('d', 'M 0,-5 L 10,0 L 0,5').attr('fill', color); + }); + } else { + defs.append('marker') + .attr('id', 'arrow-default') + .attr('viewBox', '-0 -5 10 10').attr('refX', options.nodeRadius + 10) + .attr('refY', 0).attr('orient', 'auto') + .attr('markerWidth', 6).attr('markerHeight', 6) + .append('path').attr('d', 'M 0,-5 L 10,0 L 0,5').attr('fill', '#666'); + } + + // ── Force simulation ────────────────────────────────────────────────── + const sim = d3.forceSimulation(nodes) + .force('link', d3.forceLink(links).id((d: any) => d.id).distance(options.linkDistance)) + .force('charge', d3.forceManyBody().strength(options.chargeStrength)) + .force('center', d3.forceCenter(width / 2, height / 2).strength(options.centerGravity)) + .force('collide', d3.forceCollide().radius((d: any) => nodeR(d) + 14)); + + simulationRef.current = sim; + + const g = svg.append('g').attr('class', 'graph-root'); + gRef.current = g; + + // ── Tooltip ─────────────────────────────────────────────────────────── + const tooltip = d3.select(containerRef.current) + .selectAll('.graph-tooltip').data([null]).join('div') + .attr('class', 'graph-tooltip') + .style('position', 'absolute') + .style('pointer-events', 'none') + .style('background', '#000') + .style('color', '#fff') + .style('padding', '6px 12px') + .style('font-family', '"JetBrains Mono", monospace') + .style('font-size', '11px') + .style('line-height', '1.5') + .style('opacity', 0) + .style('border', '1px solid #333') + .style('z-index', '999') + .style('max-width', '220px') + .style('word-break', 'break-word'); + + // ── Links ──────────────────────────────────────────────────────────── + const linkG = g.append('g').attr('class', 'links'); + + // Adjacency set for hover highlight + const adjacentIds = new Set(); + + // Straight lines or curved paths + const linkEl = options.showCurvedEdges + ? linkG.selectAll('path').data(links).enter().append('path') + .attr('fill', 'none') + .attr('stroke', (d: any) => { + if (!options.colorByType) return '#aaa'; + const srcNode = nodes.find(n => n.id === (typeof d.source === 'object' ? d.source.id : d.source)); + return srcNode ? (typeColorMap.current.get(srcNode.type) || '#aaa') : '#aaa'; + }) + .attr('stroke-width', 1.5) + .attr('stroke-opacity', 0.55) + .attr('marker-end', (d: any) => { + if (!options.colorByType) return 'url(#arrow-default)'; + const srcNode = nodes.find(n => n.id === (typeof d.source === 'object' ? d.source.id : d.source)); + const t = srcNode?.type?.replace(/\s+/g, '_') || 'Unknown'; + return `url(#arrow-${t})`; + }) + : linkG.selectAll('line').data(links).enter().append('line') + .attr('stroke', (d: any) => { + if (!options.colorByType) return '#aaa'; + const srcNode = nodes.find(n => n.id === (typeof d.source === 'object' ? d.source.id : d.source)); + return srcNode ? (typeColorMap.current.get(srcNode.type) || '#aaa') : '#aaa'; + }) + .attr('stroke-width', 1.5) + .attr('stroke-opacity', 0.55) + .attr('marker-end', (d: any) => { + if (!options.colorByType) return 'url(#arrow-default)'; + const srcNode = nodes.find(n => n.id === (typeof d.source === 'object' ? d.source.id : d.source)); + const t = srcNode?.type?.replace(/\s+/g, '_') || 'Unknown'; + return `url(#arrow-${t})`; + }); + + // ── Nodes ───────────────────────────────────────────────────────────── + const node = g.append('g').attr('class', 'nodes') + .selectAll('g').data(nodes).enter().append('g') + .call(d3.drag() + .on('start', (event, d) => { if (!event.active) sim.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }) + .on('drag', (event, d) => { d.fx = event.x; d.fy = event.y; }) + .on('end', (event, d) => { if (!event.active) sim.alphaTarget(0); d.fx = null; d.fy = null; }) + ) + .on('mouseover', (_, d: any) => { + // Build adjacent set + adjacentIds.clear(); + adjacentIds.add(d.id); + links.forEach(l => { + const sid = typeof l.source === 'object' ? l.source.id : l.source; + const tid = typeof l.target === 'object' ? l.target.id : l.target; + if (sid === d.id) adjacentIds.add(tid); + if (tid === d.id) adjacentIds.add(sid); + }); + // Dim non-adjacent + node.select('circle') + .style('opacity', (n: any) => adjacentIds.has(n.id) ? 1 : 0.15) + .style('stroke-width', (n: any) => n.id === d.id ? 4 : 2); + (linkEl as any) + .style('stroke-opacity', (l: any) => { + const sid = typeof l.source === 'object' ? l.source.id : l.source; + const tid = typeof l.target === 'object' ? l.target.id : l.target; + return (sid === d.id || tid === d.id) ? 0.9 : 0.04; + }); + tooltip + .style('opacity', 1) + .html(`${d.label}
ID: ${d.id}
Type: ${d.type || '—'}
Degree: ${degreeMap.get(d.id) || 0}`); + }) + .on('mousemove', (event) => { + const rect = containerRef.current!.getBoundingClientRect(); + tooltip + .style('left', (event.clientX - rect.left + 14) + 'px') + .style('top', (event.clientY - rect.top - 32) + 'px'); + }) + .on('mouseout', () => { + adjacentIds.clear(); + node.select('circle') + .style('opacity', (n: any) => { + if (!highlightNodeIds || highlightNodeIds.size === 0) return 1; + return highlightNodeIds.has(n.id) ? 1 : 0.2; + }) + .style('stroke-width', (n: any) => highlightNodeIds?.has(n.id) ? 4 : 2); + (linkEl as any).style('stroke-opacity', 0.55); + tooltip.style('opacity', 0); + }) + .on('click', (event, d: any) => { + setActiveNode(d); + // Zoom to node on single click + if (!svgRef.current || !zoomRef.current) return; + const w = svgRef.current.clientWidth; + const h = svgRef.current.clientHeight; + const t = d3.zoomIdentity.translate(w / 2 - d.x, h / 2 - d.y).scale(1.4); + d3.select(svgRef.current).transition().duration(500).call(zoomRef.current.transform, t); + event.stopPropagation(); + }) + .on('dblclick', (event, d: any) => { + const newName = window.prompt('Update entity name:', d.label); + if (newName && newName.trim() && newName.trim() !== d.label) { + const updated = newName.trim(); + d.label = updated; + d3.select(event.currentTarget).select('text.node-label').text( + updated.length > 18 ? updated.substring(0, 16) + '…' : updated + ); + if (onNodeUpdate) onNodeUpdate(d.id, updated); + } + }); + + // Circle + node.append('circle') + .attr('r', (d: any) => nodeR(d)) + .attr('fill', (d: any) => options.colorByType + ? (typeColorMap.current.get(d.type) || '#ccc') + : '#fff') + .attr('stroke', (d: any) => { + if (highlightNodeIds && highlightNodeIds.size > 0) { + return highlightNodeIds.has(d.id) ? '#ff0' : (options.colorByType + ? d3.color(typeColorMap.current.get(d.type) || '#ccc')!.darker(1).toString() + : '#000'); + } + return options.colorByType + ? d3.color(typeColorMap.current.get(d.type) || '#ccc')!.darker(1).toString() + : '#000'; + }) + .attr('stroke-width', (d: any) => highlightNodeIds?.has(d.id) ? 4 : 2) + .style('opacity', (d: any) => { + if (!highlightNodeIds || highlightNodeIds.size === 0) return 1; + return highlightNodeIds.has(d.id) ? 1 : 0.2; + }) + .style('filter', 'drop-shadow(1px 2px 3px rgba(0,0,0,0.15))') + .style('cursor', 'pointer'); + + // Type abbreviation inside circle + node.append('text') + .attr('class', 'node-type-badge') + .text((d: any) => (d.type || '?').substring(0, 2).toUpperCase()) + .attr('text-anchor', 'middle').attr('dy', '0.35em') + .style('font-family', '"JetBrains Mono", monospace') + .style('font-size', (d: any) => `${Math.max(8, nodeR(d) - 6)}px`) + .style('font-weight', '700') + .style('fill', (d: any) => { + if (!options.colorByType) return '#000'; + const c = d3.color(typeColorMap.current.get(d.type) || '#ccc'); + if (!c) return '#000'; + const { r, g: gv, b } = c.rgb(); + return (r * 0.299 + gv * 0.587 + b * 0.114) > 150 ? '#111' : '#fff'; + }) + .style('pointer-events', 'none'); + + // Node name label below circle + if (options.showLabels) { + node.append('text') + .attr('class', 'node-label') + .text((d: any) => d.label && d.label.length > 18 ? d.label.substring(0, 16) + '…' : d.label) + .attr('text-anchor', 'middle') + .attr('dy', (d: any) => nodeR(d) + 14) + .style('font-family', '"JetBrains Mono", monospace') + .style('font-size', '10px') + .style('font-weight', '600') + .style('fill', '#222') + .style('pointer-events', 'none'); + } + + // Edge labels + let edgeLabel: d3.Selection | null = null; + if (options.showEdgeLabels) { + edgeLabel = g.append('g').attr('class', 'edge-labels') + .selectAll('text').data(links).enter().append('text') + .text((d: any) => d.type || '') + .style('font-family', '"JetBrains Mono", monospace') + .style('font-size', '9px') + .style('fill', '#777') + .style('text-anchor', 'middle') + .style('pointer-events', 'none') + .attr('dy', -5); + } + + // ── Tick ────────────────────────────────────────────────────────────── + sim.on('tick', () => { + if (options.showCurvedEdges) { + (linkEl as d3.Selection) + .attr('d', (d: any) => { + const sx = d.source.x, sy = d.source.y; + const tx = d.target.x, ty = d.target.y; + const dx = tx - sx, dy = ty - sy; + const dr = Math.sqrt(dx * dx + dy * dy) * 0.8; + return `M${sx},${sy}A${dr},${dr} 0 0,1 ${tx},${ty}`; + }); + } else { + (linkEl as d3.Selection) + .attr('x1', (d: any) => d.source.x).attr('y1', (d: any) => d.source.y) + .attr('x2', (d: any) => d.target.x).attr('y2', (d: any) => d.target.y); + } + node.attr('transform', (d: any) => `translate(${d.x},${d.y})`); + if (edgeLabel) { + edgeLabel + .attr('x', (d: any) => (d.source.x + d.target.x) / 2) + .attr('y', (d: any) => (d.source.y + d.target.y) / 2); + } + }); + + // ── Zoom / Pan ──────────────────────────────────────────────────────── + const zoom = d3.zoom() + .scaleExtent([0.03, 8]) + .on('zoom', (event) => g.attr('transform', event.transform)); + + svg.call(zoom); + // Click on SVG background resets highlighting and active node + svg.on('click', () => { + setActiveNode(null); + node.select('circle').style('opacity', 1).style('stroke-width', 2); + (linkEl as any).style('stroke-opacity', 0.55); + }); + + zoomRef.current = zoom; + + return () => { sim.stop(); }; + }, [data, options, highlightNodeIds]); + + return ( +
+ + + {/* ── Node details modal ────────────────────────────────────────────── */} + {activeNode && ( +
+
+
+

NODE DETAILS

+ + {activeNode.type || 'Unknown'} + +
+ +
+ +
+
+ Name: + {activeNode.label || '—'} +
+
+ UUID: + {activeNode.id} +
+ + {activeNode.properties && Object.keys(activeNode.properties).length > 0 && ( + <> +
+
PROPERTIES
+
+ {Object.entries(activeNode.properties).map(([k, v]) => ( +
+ {k}: + {String(v)} +
+ ))} +
+ + )} + + {activeNode.description && ( + <> +
+
DESCRIPTION / SUMMARY
+
+ {activeNode.description} +
+ + )} +
+
+ )} + + +
+ ); + } +); + +GraphCanvas.displayName = 'GraphCanvas'; +export default GraphCanvas; diff --git a/frontend-react/src/context/AuthContext.tsx b/frontend-react/src/context/AuthContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9071d31872550cdad81d0d7800d27c37bf5e6c79 --- /dev/null +++ b/frontend-react/src/context/AuthContext.tsx @@ -0,0 +1,64 @@ +import React, { createContext, useContext, useState, useEffect } from 'react'; + +// This acts as our authentication context for the React application + +interface User { + username: string; + email?: string; + full_name?: string; + scopes: string[]; +} + +interface AuthContextType { + token: string | null; + user: User | null; + login: (token: string, user: User) => void; + logout: () => void; + isAuthenticated: boolean; +} + +const AuthContext = createContext(undefined); + +export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [token, setToken] = useState(localStorage.getItem('token')); + const [user, setUser] = useState(null); + + useEffect(() => { + const storedUser = localStorage.getItem('user'); + if (storedUser) { + try { + setUser(JSON.parse(storedUser)); + } catch (e) { + console.error('Failed to parse stored user', e); + } + } + }, []); + + const login = (newToken: string, newUser: User) => { + localStorage.setItem('token', newToken); + localStorage.setItem('user', JSON.stringify(newUser)); + setToken(newToken); + setUser(newUser); + }; + + const logout = () => { + localStorage.removeItem('token'); + localStorage.removeItem('user'); + setToken(null); + setUser(null); + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css new file mode 100644 index 0000000000000000000000000000000000000000..76a507ce7f82b0753729ef58b3f76e508eb22d77 --- /dev/null +++ b/frontend-react/src/index.css @@ -0,0 +1,494 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Noto+Sans+SC:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600;700&display=swap'); +@import "tailwindcss"; + +/* ── Design Tokens ─────────────────────────────────────────────────────── */ +:root { + --bg-color: #ffffff; + --text-color: #000000; + --border-color: #000000; + --surface-color: #f5f5f5; /* subtle off-white for secondary surfaces */ + --hover-bg: #f0f0f0; + --muted-color: #666666; + --success-color: #16a34a; + --error-color: #dc2626; + --warning-color: #d97706; + + --font-sans: 'Inter', 'Noto Sans SC', sans-serif; + --font-display: 'Space Grotesk', 'Inter', sans-serif; + --font-mono: 'JetBrains Mono', monospace; + + --transition-speed: 0.18s; + --nav-height: 60px; +} + +/* ── Reset ─────────────────────────────────────────────────────────────── */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: var(--font-sans); + background-color: var(--bg-color); + color: var(--text-color); + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + min-height: 100vh; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-display); + font-weight: 600; + margin-bottom: 0.75rem; + line-height: 1.2; +} + +code, pre, .technical { font-family: var(--font-mono); } + +/* ── Links ─────────────────────────────────────────────────────────────── */ +a { + color: var(--text-color); + text-decoration: underline; + text-underline-offset: 3px; + transition: background var(--transition-speed), color var(--transition-speed); +} +a:hover { + background-color: var(--text-color); + color: var(--bg-color); +} + +/* ── Buttons ───────────────────────────────────────────────────────────── */ +button, .app-btn { + font-family: var(--font-display); + background-color: var(--bg-color); + color: var(--text-color); + border: 2px solid var(--border-color); + padding: 0.5rem 1rem; + font-weight: 600; + cursor: pointer; + transition: background var(--transition-speed) ease, color var(--transition-speed) ease; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.8px; + display: inline-flex; + align-items: center; + gap: 0.4rem; +} +button:hover, .app-btn:hover { + background-color: var(--text-color); + color: var(--bg-color); +} +button:disabled, .app-btn:disabled { + opacity: 0.45; + cursor: not-allowed; + pointer-events: none; +} + +/* Text-only button (no border, no background) */ +.text-btn { + background: none !important; + border: none !important; + color: var(--text-color); + text-decoration: underline; + text-underline-offset: 3px; + cursor: pointer; + font-family: var(--font-mono); + font-size: 0.8rem; + font-weight: 600; + transition: background var(--transition-speed), color var(--transition-speed); + padding: 0.2rem 0.4rem; +} +.text-btn:hover { + background: var(--text-color) !important; + color: var(--bg-color) !important; +} + +/* ── Toast dismiss button ──────────────────────────────────────────────── */ +/* Prevents global button:hover from flipping the close button inside toasts */ +.toast-dismiss-btn { + background: transparent !important; + border: none !important; + color: inherit !important; + cursor: pointer !important; + margin-left: auto !important; + padding: 0 0.3rem !important; + font-size: 1.2rem !important; + line-height: 1 !important; + opacity: 0.7; + flex-shrink: 0; + transition: opacity 0.12s ease !important; +} +.toast-dismiss-btn:hover { + background: transparent !important; + color: inherit !important; + opacity: 1 !important; +} + +/* ── Status toast ──────────────────────────────────────────────────────── */ +.status-toast { + position: fixed; + bottom: 2rem; + right: 2rem; + background: var(--text-color); + color: var(--bg-color); + padding: 0.9rem 1.2rem; + border-left: 5px solid #000; + box-shadow: 4px 4px 0 rgba(0,0,0,0.3); + display: flex; + align-items: center; + gap: 0.75rem; + z-index: 9999; + font-family: var(--font-mono); + font-weight: 700; + font-size: 0.85rem; + max-width: 420px; + animation: slideUpToast 0.28s ease-out both; + will-change: transform; +} +.status-toast.error { border-left-color: var(--error-color); } +.status-toast.success { border-left-color: var(--success-color); } + +/* ── Form Controls ─────────────────────────────────────────────────────── */ +input, textarea, select { + font-family: var(--font-sans); + background-color: var(--bg-color); + color: var(--text-color); + border: 2px solid var(--border-color); + padding: 0.5rem 0.75rem; + font-size: 0.95rem; + transition: box-shadow var(--transition-speed) ease; +} +input:focus, textarea:focus, select:focus { + outline: none; + box-shadow: 3px 3px 0 var(--border-color); +} + +/* ── Scrollbars ────────────────────────────────────────────────────────── */ +::-webkit-scrollbar { width: 8px; height: 8px; } +::-webkit-scrollbar-track { background: #f9f9f9; border-left: 1px solid #e5e5e5; } +::-webkit-scrollbar-thumb { background: #ccc; } +::-webkit-scrollbar-thumb:hover { background: #999; } + +/* ── Layout Utilities ──────────────────────────────────────────────────── */ +.container { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; +} + +.card { + border: 2px solid var(--border-color); + padding: 1.5rem; + background-color: var(--bg-color); + transition: transform var(--transition-speed) ease, box-shadow var(--transition-speed) ease; +} +.card:hover { + transform: translateY(-2px); + box-shadow: 5px 5px 0 var(--border-color); +} + +.mono-text { font-family: var(--font-mono); font-size: 0.88em; } + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; +} + +.full-width { width: 100%; } + +.page-header { + border-bottom: 3px solid var(--border-color); + padding-bottom: 1rem; + margin-bottom: 2rem; +} + +/* ── Keyframes ─────────────────────────────────────────────────────────── */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slideUpToast { + from { transform: translateY(80px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +@keyframes slideUp { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +@keyframes spin { + 100% { transform: rotate(360deg); } +} + +.fade-in { animation: fadeIn 0.4s ease both; } + +/* ── Status indicators ─────────────────────────────────────────────────── */ +.status-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} +.status-dot.online { background: var(--success-color); } +.status-dot.offline { background: var(--error-color); } + +/* ── Responsive ────────────────────────────────────────────────────────── */ +@media (max-width: 768px) { + .container { padding: 1rem; } + .card { padding: 1rem; } +} + +/* ── Admin / shared aliases ─────────────────────────────────────────────── */ +/* These classes are referenced in AdminDashboard and other views. */ +/* They map onto the existing design system so everything is uniform. */ + +/* Base btn — same as global button but without uppercase transform */ +.btn { + font-family: var(--font-display); + background-color: var(--bg-color); + color: var(--text-color); + border: 2px solid var(--border-color); + padding: 0.5rem 1rem; + font-weight: 600; + cursor: pointer; + font-size: 0.85rem; + letter-spacing: 0.5px; + display: inline-flex; + align-items: center; + gap: 0.4rem; + transition: background var(--transition-speed) ease, color var(--transition-speed) ease; +} +.btn:hover:not(:disabled) { + background-color: var(--text-color); + color: var(--bg-color); +} +.btn:disabled { + opacity: 0.45; + cursor: not-allowed; + pointer-events: none; +} + +/* Primary button — filled black */ +.btn-primary { + background-color: var(--text-color); + color: var(--bg-color); + border: 2px solid var(--text-color); +} +.btn-primary:hover:not(:disabled) { + background-color: #333; + border-color: #333; +} + +/* Outline / ghost button */ +.btn-outline { + background: transparent; + color: var(--text-color); + border: 2px solid var(--border-color); +} +.btn-outline:hover:not(:disabled) { + background: var(--text-color); + color: var(--bg-color); +} + +/* Danger button */ +.btn-danger { + color: var(--error-color); + border-color: var(--error-color); +} +.btn-danger:hover:not(:disabled) { + background: var(--error-color); + color: #fff; +} + +/* Uniform search / select input (matches global input but explicit class) */ +.search-input { + font-family: var(--font-sans); + background-color: var(--bg-color); + color: var(--text-color); + border: 2px solid var(--border-color); + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + transition: box-shadow var(--transition-speed) ease; + width: 100%; +} +.search-input:focus { + outline: none; + box-shadow: 3px 3px 0 var(--border-color); +} + +/* Status / metric cards */ +.status-card { + border: 2px solid var(--border-color); + padding: 1.25rem 1.5rem; + background: var(--bg-color); +} + +.metric-value { + font-family: var(--font-mono); + font-size: 2rem; + font-weight: 900; + line-height: 1; + margin-top: 0.25rem; +} + +.status-label { + font-family: var(--font-mono); + font-size: 0.68rem; + font-weight: 700; + color: var(--muted-color); + letter-spacing: 1px; + text-transform: uppercase; +} + +/* Online / offline indicator dots */ +.indicator { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; +} +.indicator.online { background: var(--success-color); } +.indicator.offline { background: var(--error-color); } +.indicator.pending { background: var(--warning-color); } + +/* Typography */ +.title-lg { + font-family: var(--font-display); + font-size: 1.8rem; + font-weight: 700; + line-height: 1.1; +} +.title-md { + font-family: var(--font-display); + font-size: 1.15rem; + font-weight: 700; +} +.title-sm { + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 1px; + text-transform: uppercase; + color: var(--muted-color); +} + +/* Data table */ +.data-table { + width: 100%; + border-collapse: collapse; + font-size: 0.9rem; +} +.data-table thead tr { + background: var(--surface-color); + border-bottom: 2px solid var(--border-color); +} +.data-table th, +.data-table td { + padding: 0.75rem 1rem; + text-align: left; + border-bottom: 1px solid #eaeaea; +} +.data-table th { + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.8px; + text-transform: uppercase; + color: var(--muted-color); +} +.data-table tbody tr:hover { + background: var(--surface-color); +} +.data-table tbody tr:last-child td { + border-bottom: none; +} + +/* Tag / badge chips */ +.chip { + display: inline-flex; + align-items: center; + gap: 4px; + border: 1.5px solid var(--border-color); + padding: 2px 8px; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; +} +.chip.success { border-color: var(--success-color); color: var(--success-color); } +.chip.error { border-color: var(--error-color); color: var(--error-color); } +.chip.warning { border-color: var(--warning-color); color: var(--warning-color); } +.chip.filled { background: var(--text-color); color: var(--bg-color); } + +/* Empty state */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 4rem 2rem; + color: var(--muted-color); + gap: 0.75rem; + border: 2px dashed var(--border-color); + font-family: var(--font-mono); + font-size: 0.85rem; + letter-spacing: 0.5px; + text-align: center; +} + +/* Help tooltip */ +.help-tooltip { + position: relative; + display: inline-flex; + align-items: center; +} +.help-tooltip [data-tip] { + cursor: help; + color: var(--muted-color); +} +.help-tooltip [data-tip]:hover::after { + content: attr(data-tip); + position: absolute; + bottom: 125%; + left: 50%; + transform: translateX(-50%); + background: var(--text-color); + color: var(--bg-color); + padding: 0.4rem 0.75rem; + font-family: var(--font-mono); + font-size: 0.75rem; + white-space: nowrap; + z-index: 9999; + pointer-events: none; + max-width: 260px; + white-space: normal; + text-align: center; +} + +/* Page info bar */ +.page-info-bar { + background: var(--surface-color); + border-left: 4px solid var(--border-color); + padding: 0.75rem 1rem; + font-size: 0.82rem; + line-height: 1.6; + margin-bottom: 1.5rem; + display: flex; + align-items: flex-start; + gap: 0.75rem; + color: var(--muted-color); +} +.page-info-bar strong { color: var(--text-color); font-family: var(--font-mono); } diff --git a/frontend-react/src/main.tsx b/frontend-react/src/main.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bef5202a32cbd0632c43de40f6e908532903fd42 --- /dev/null +++ b/frontend-react/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/frontend-react/src/types/api.ts b/frontend-react/src/types/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4866a0c138be56dd392aa93f67743e17843587d --- /dev/null +++ b/frontend-react/src/types/api.ts @@ -0,0 +1,183 @@ +export interface LoginRequest { + username: string; + password: string; +} + +export interface RegisterRequest { + username: string; + password: string; + email?: string; + full_name?: string; + scopes?: string[]; + tenant_id?: string; +} + +export interface TokenResponse { + access_token: string; + token_type: string; +} + +export interface DocumentUploadResponse { + document_id: string; + filename: string; + size_bytes: number; + task_id?: string; + message: string; +} + +export interface ScrapeRequest { + url: string; +} + +export interface CrawlRequest { + url: string; + max_depth?: number; + max_pages?: number; +} + +export interface IngestionStatusResponse { + task_id: string; + status: string; + progress?: Record; + result?: Record; +} + +export interface DocumentInfo { + id: string; + filename: string; + file_type: string; + size_bytes: number; + upload_date: string; +} + +export interface DocumentListResponse { + documents: DocumentInfo[]; + total: number; +} + +export interface QueryRequest { + query: string; + top_k?: number; + streaming?: boolean; + document_id?: string; + conversation_id?: string; + use_got?: boolean; + at_time?: string; +} + +export interface ConfidenceJudgmentResponse { + score: number; + reasoning: string; + grounded_claims: number; + ungrounded_claims: number; + hallucination_risk: 'low' | 'medium' | 'high'; +} + +export interface QueryResponse { + answer: string; + sources: Array>; + reasoning_chain: string[]; + confidence: number; + confidence_judgment?: ConfidenceJudgmentResponse; + retrieval_method: string; + processing_time_seconds: number; + conversation_id?: string; + drift_expanded?: boolean; + total_sub_queries?: number; +} + +export interface EvalResultData { + overall_score?: number; + faithfulness: number; + answer_relevancy?: number; + relevancy?: number; + context_precision?: number; + precision?: number; +} + +export interface Message { + id?: string; + role: string; + content: string; + reasoning?: string[]; + sources?: Array>; + confidence?: number | null; + hallucination_risk?: string; + confidence_reasoning?: string; + created_at?: string; + eval_result?: EvalResultData; + evaluating?: boolean; + drift_expanded?: boolean; +} + +export interface Conversation { + id: string; + title: string; + created_at: string; + updated_at: string; + messages?: Message[]; +} + +export interface ConversationListResponse { + conversations: Conversation[]; +} + +export interface OntologyResponse { + version: string; + entity_types: string[]; + relationship_types: string[]; + properties: Record; + created_at: string; + approved: boolean; +} + +export interface OntologyUpdateRequest { + entity_types?: string[]; + relationship_types?: string[]; + properties?: Record; + approved?: boolean; +} + +export interface GraphNode { + id: string; + label: string; + type: string; + description?: string; + properties: Record; + community_id?: number; + valid_from?: string; + valid_until?: string; +} + +export interface GraphEdge { + source: string; + target: string; + type: string; + properties: Record; + valid_from?: string; + confidence?: number; +} + +export interface GraphVisualizationResponse { + nodes: GraphNode[]; + edges: GraphEdge[]; +} + +export interface SystemHealthResponse { + status: string; + version: string; + neo4j_connected: boolean; + redis_connected: boolean; + workers_active: number; + timestamp: string; +} + +export interface SystemStatsResponse { + documents_count: number; + entities_count: number; + relationships_count: number; + chunks_count: number; + ontology_version: string; +} + +export interface DriftReport { id: string; detected_at: string; new_entity_types: string[]; new_relationship_types: string[]; removed_entity_types: string[]; removed_relationship_types: string[]; sample_size: number; drift_score: number; status: string; approved_by?: string; approved_at?: string; } diff --git a/frontend-react/src/views/AdminDashboard.tsx b/frontend-react/src/views/AdminDashboard.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a08ea36bb7b2fa807029d93e126a41101f14ce32 --- /dev/null +++ b/frontend-react/src/views/AdminDashboard.tsx @@ -0,0 +1,762 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { useAuth } from '../context/AuthContext'; +import { + BarChart2, Cpu, Users, Database, Settings, + Trash2, Check, X, Play, RefreshCw, Shield, + AlertTriangle, Zap, GitBranch, Info +} from 'lucide-react'; + +const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api'; + +// ─── Helpers ────────────────────────────────────────────────────────────────── +const Spinner = () => ( + +); + +// ─── Tab Components ────────────────────────────────────────────────────────── + +const OverviewTab = ({ stats, health, onRefresh }: { stats: Partial; health: import('../types/api').SystemHealthResponse | any; onRefresh: () => void }) => ( +
+ {/* KPI Grid */} +
+ {[ + { label:'Graph Nodes', value: stats?.graph?.nodes ?? stats?.total_nodes ?? '—', icon: }, + { label:'Relationships', value: stats?.graph?.relationships ?? stats?.total_relationships ?? '—', icon: }, + { label:'Documents', value: stats?.documents?.total ?? stats?.total_documents ?? '—', icon: }, + { label:'Est. LLM Cost', value: `$${(stats?.costs?.total_estimated_usd ?? 0).toFixed(4)}`, icon: }, + ].map(c => ( +
+
+
{c.label}
+ {c.icon} +
+
{c.value}
+
+ ))} +
+ + {/* System health */} +
+
+

System Health

+ +
+ {health ? ( +
+ {Object.entries(health).map(([k, v]: [string, any]) => { + const isOk = v === true || v === 'ok' || v === 'connected' || v === 'healthy'; + const isErr = v === false || v === 'error' || v === 'disconnected'; + return ( +
+ +
+
{k.toUpperCase()}
+
+ {typeof v === 'object' ? JSON.stringify(v) : String(v)} +
+
+
+ ); + })} +
+ ) : ( +
+ Loading health data… +
+ )} +
+ + {/* Provider info */} + {stats?.system && ( +
+

LLM Provider

+
+ {Object.entries(stats.system).map(([k, v]: any) => ( +
{k}: {String(v)}
+ ))} +
+
+ )} +
+); + +const UsersTab = ({ token }: { token: string | null }) => { + const [users, setUsers] = useState([]); + const [loading, setLoading] = useState(true); + const [msg, setMsg] = useState(''); + + const fetchUsers = useCallback(async () => { + setLoading(true); + try { + const res = await fetch(`${API_BASE}/admin/users`, { headers: { Authorization: `Bearer ${token}` } }); + if (res.ok) { + const json = await res.json(); + setUsers(json.users || []); + } + } finally { setLoading(false); } + }, [token]); + + useEffect(() => { fetchUsers(); }, [fetchUsers]); + + const updateRole = async (username: string, scopes: string) => { + const res = await fetch(`${API_BASE}/admin/users/${username}/role`, { + method: 'PUT', + headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ scopes: [scopes] }) + }); + if (res.ok) { + setUsers(u => u.map(usr => usr.username === username ? { ...usr, scopes: [scopes] } : usr)); + setMsg(`Role updated for ${username}`); + setTimeout(() => setMsg(''), 3000); + } else { + setMsg(`Failed to update role for ${username}`); + setTimeout(() => setMsg(''), 3000); + } + }; + + return ( +
+
+

Registered Users

+ +
+ {msg &&
{msg}
} + {loading ? ( +
+ ) : ( +
+ + + + {users.map(u => { + const isAdminUser = u.username === 'admin'; + const currentScope = u.scopes?.includes('admin') ? 'admin' : (u.scopes?.includes('write') ? 'write' : 'read'); + return ( + + + + + + ); + })} + {users.length === 0 && ( + + )} + +
UsernameScopeChange Role
+ {u.username} + {isAdminUser && PROTECTED} + {u.scopes?.join(', ') || 'none'} + {isAdminUser ? ( + + ) : ( + + )} +
No users found.
+
+ )} +
+ ); +}; + +const DocumentsTab = ({ token }: { token: string | null }) => { + const [docs, setDocs] = useState([]); + const [loading, setLoading] = useState(true); + const [msg, setMsg] = useState(''); + + const fetchDocs = useCallback(async () => { + setLoading(true); + try { + const res = await fetch(`${API_BASE}/admin/documents`, { headers: { Authorization: `Bearer ${token}` } }); + if (res.ok) { + const json = await res.json(); + setDocs(json.documents || []); + } + } finally { setLoading(false); } + }, [token]); + + useEffect(() => { fetchDocs(); }, [fetchDocs]); + + const deleteDoc = async (id: string, filename: string) => { + if (!window.confirm(`Delete "${filename}" and all its graph data? This cannot be undone.`)) return; + const res = await fetch(`${API_BASE}/admin/documents/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } }); + if (res.ok) { + setDocs(d => d.filter(doc => doc.id !== id)); + setMsg('Document deleted.'); + setTimeout(() => setMsg(''), 3000); + } + }; + + const reIngestDoc = async (id: string, filename: string) => { + setMsg(`Re-ingesting "${filename}"...`); + try { + const res = await fetch(`${API_BASE}/admin/documents/${id}/reingest`, { + method: 'POST', + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + const data = await res.json(); + setMsg(`Re-ingestion queued for "${filename}". Task: ${data.task_id?.slice(0,8)}…`); + setTimeout(() => { setMsg(''); fetchDocs(); }, 5000); + } else { + setMsg(`Failed to re-ingest "${filename}"`); + setTimeout(() => setMsg(''), 3000); + } + } catch { + setMsg('Network error during re-ingest.'); + setTimeout(() => setMsg(''), 3000); + } + }; + + return ( +
+
+

Document Vault

+ {docs.length} documents +
+ {msg &&
{msg}
} + {loading ? ( +
+ ) : ( +
+ + + + {docs.map(d => ( + + + + + + + ))} + {docs.length === 0 && ( + + )} + +
IDFilenameStatusActions
{d.id?.substring(0,12)}…{d.filename} + + {d.status || 'unknown'} + + + {(d.status === 'failed' || d.status === 'pending') && ( + + )} + +
No documents uploaded yet.
+
+ )} +
+ ); +}; + +const GraphCRUDTab = ({ token }: { token: string | null }) => { + const [nodes, setNodes] = useState([]); + const [query, setQuery] = useState(''); + const [loading, setLoading] = useState(false); + const [msg, setMsg] = useState(''); + + const search = async (e?: React.FormEvent) => { + e?.preventDefault(); + setLoading(true); + try { + const res = await fetch(`${API_BASE}/admin/graph/nodes?query=${encodeURIComponent(query)}&limit=100`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) setNodes((await res.json()).nodes || []); + } finally { setLoading(false); } + }; + + const deleteNode = async (id: string) => { + if (!window.confirm('Detach and delete this node?')) return; + const res = await fetch(`${API_BASE}/admin/graph/nodes/${id}`, { + method: 'DELETE', headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + setNodes(n => n.filter(nd => nd.id !== id)); + setMsg('Node deleted.'); + setTimeout(() => setMsg(''), 3000); + } + }; + + return ( +
+

Graph Node Browser

+
+ + Search and inspect nodes directly in Neo4j. Use label names or property values. DELETE detaches all relationships before removing the node. +
+ {msg &&
{msg}
} +
+ setQuery(e.target.value)} + placeholder="Search node labels or properties…" className="search-input flex-1"/> + +
+
+ + + + {nodes.map((n, i) => ( + + + + + + + ))} + {nodes.length === 0 && ( + + )} + +
IDLabelsPropertiesAction
{n.id}{n.labels?.join(', ')} + {JSON.stringify(n.properties)} + + +
+ {loading ? 'Searching…' : 'Enter a search term above to browse nodes.'} +
+
+
+ ); +}; + +const OntologyGovernanceTab = ({ token }: { token: string | null }) => { + const [proposals, setProposals] = useState([]); + const [driftReports, setDriftReports] = useState([]); + const [loading, setLoading] = useState(true); + const [detectLoading, setDetectLoading] = useState(false); + const [msg, setMsg] = useState(''); + + const fetchData = useCallback(async () => { + setLoading(true); + try { + const [propRes, driftRes] = await Promise.all([ + fetch(`${API_BASE}/admin/ontology/pending`, { headers: { Authorization: `Bearer ${token}` } }), + fetch(`${API_BASE}/ontology/drift`, { headers: { Authorization: `Bearer ${token}` } }), + ]); + if (propRes.ok) setProposals((await propRes.json()).proposals || []); + if (driftRes.ok) setDriftReports((await driftRes.json()).reports || []); + } finally { setLoading(false); } + }, [token]); + + useEffect(() => { fetchData(); }, [fetchData]); + + const handleProposal = async (id: string, action: 'approve' | 'reject') => { + const res = await fetch(`${API_BASE}/admin/ontology/${action}/${id}`, { + method: 'POST', headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + setProposals(p => p.filter(o => o.id !== id)); + setMsg(`Proposal ${action}d.`); + setTimeout(() => setMsg(''), 3000); + } + }; + + const handleDrift = async (id: string, action: 'approve' | 'reject') => { + const res = await fetch(`${API_BASE}/ontology/drift/${id}/${action}`, { + method: 'POST', headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + setDriftReports(d => d.filter(r => r.id !== id)); + setMsg(`Drift report ${action}d.`); + setTimeout(() => setMsg(''), 3000); + } + }; + + const detectDrift = async () => { + setDetectLoading(true); + try { + const res = await fetch(`${API_BASE}/ontology/drift/detect`, { + method: 'POST', headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + setMsg('Drift detection complete. Refreshing…'); + await fetchData(); + } + } finally { + setDetectLoading(false); + setTimeout(() => setMsg(''), 4000); + } + }; + + return ( +
+ {msg &&
{msg}
} + + {/* Drift detection */} +
+
+

Ontology Drift Reports

+ +
+

+ Drift detection samples recent data and suggests additions or changes to the graph schema. + Review and approve or reject proposals below. +

+ {loading ?
: ( +
+ {driftReports.map(r => ( +
+
+
+ {r.status || 'pending'} + {r.new_entity_types?.length || 0} new types +
+

+ {r.summary || 'Drift report — review suggested schema changes.'} +

+
+
+ + +
+
+ ))} + {driftReports.length === 0 && ( +
No pending drift reports. Run drift detection above.
+ )} +
+ )} +
+ + {/* Manual proposals */} +
+

Manual Schema Proposals

+
+ {proposals.map(o => ( +
+
+ {o.type} + {o.name} +
+
+ + +
+
+ ))} + {proposals.length === 0 && ( +
No pending manual proposals.
+ )} +
+
+
+ ); +}; + +const WorkersTab = ({ token }: { token: string | null }) => { + const [tasks, setTasks] = useState(null); + const [health, setHealth] = useState(null); + const [loading, setLoading] = useState(true); + + const fetchAll = useCallback(async () => { + setLoading(true); + try { + const [taskRes, healthRes] = await Promise.all([ + fetch(`${API_BASE}/admin/tasks`, { headers: { Authorization: `Bearer ${token}` } }), + fetch(`${API_BASE}/system/health`, { headers: { Authorization: `Bearer ${token}` } }), + ]); + if (taskRes.ok) setTasks(await taskRes.json()); + if (healthRes.ok) setHealth(await healthRes.json()); + } finally { setLoading(false); } + }, [token]); + + useEffect(() => { fetchAll(); }, [fetchAll]); + + return ( +
+
+
+

Celery Worker Status

+ +
+ {loading ?
: ( +
+ {[ + { label:'Active Tasks', value: tasks?.active_tasks ?? tasks?.active ?? 0 }, + { label:'Queued Tasks', value: tasks?.queued_tasks ?? tasks?.reserved ?? 0 }, + { label:'Completed', value: tasks?.completed_tasks ?? tasks?.total ?? 0 }, + { label:'Failed', value: tasks?.failed_tasks ?? 0 }, + ].map(m => ( +
+
{m.label}
+
{m.value}
+
+ ))} +
+ )} +
+ {health && ( +
+

Service Health

+
+ {Object.entries(health).map(([k, v]: any) => { + const ok = v === true || v === 'ok' || v === 'connected' || v === 'healthy'; + return ( +
+ +
+
{k}
+
{String(v)}
+
+
+ ); + })} +
+
+ )} +
+ ); +}; + +const EnrichmentTab = ({ token }: { token: string | null }) => { + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(null); + const [batchSize, setBatchSize] = useState(20); + const [minConnections, setMinConnections] = useState(1); + const [driftLoading, setDriftLoading] = useState(false); + const [driftResult, setDriftResult] = useState(null); + + const runEnrichment = async () => { + setLoading(true); setResult(null); + try { + const res = await fetch(`${API_BASE}/entities/enrich`, { + method:'POST', + headers:{ Authorization:`Bearer ${token}`, 'Content-Type':'application/json' }, + body: JSON.stringify({ batch_size: batchSize, min_connections: minConnections }) + }); + if (res.ok) setResult(await res.json()); + } finally { setLoading(false); } + }; + + const runDrift = async () => { + setDriftLoading(true); setDriftResult(null); + try { + const res = await fetch(`${API_BASE}/ontology/drift/detect`, { + method:'POST', headers:{ Authorization:`Bearer ${token}` } + }); + if (res.ok) setDriftResult(await res.json()); + } finally { setDriftLoading(false); } + }; + + return ( +
+ {/* Entity Enrichment */} +
+

Entity Enrichment

+

+ Synthesize rich LLM-generated profiles for all eligible entities by scanning their neighborhood context in the graph. +

+
+
+ + setBatchSize(Number(e.target.value))} + className="search-input w-full"/> +
+
+ + setMinConnections(Number(e.target.value))} + className="search-input w-full"/> +
+
+ + {result && ( +
+ ✓ {result.message || `Enriched ${result.enriched_count ?? '?'} entities`} +
+ )} +
+ + {/* Drift Detection */} +
+

Ontology Drift Detection

+

+ Analyse recent data samples to detect schema evolution and generate a drift report for admin review. +

+ + {driftResult && ( +
+ ✓ Drift report created. ID: {driftResult.report_id || driftResult.id || '—'} → Review in Ontology Governance tab. +
+ )} +
+
+ ); +}; + +const SandboxTab = ({ token }: { token: string | null }) => { + const [loading, setLoading] = useState(false); + const [msg, setMsg] = useState(''); + + const trigger = async (endpoint: string, label: string) => { + setLoading(true); setMsg(''); + try { + const res = await fetch(`${API_BASE}${endpoint}`, { + method:'POST', headers:{ Authorization:`Bearer ${token}` } + }); + setMsg(res.ok ? `✓ ${label} dispatched to Celery worker.` : `✗ Failed to trigger ${label}.`); + } catch { + setMsg(`✗ Network error.`); + } finally { setLoading(false); } + }; + + return ( +
+

MiroFish God-Mode Sandbox

+

+ Control the simulation loops that connect Knowledge Graph entities into living agents. +

+ {msg && ( +
+ {msg} +
+ )} +
+ {[ + { endpoint:'/v1/simulation/generate_personas', label:'Generate Agent Personas', icon:, + desc:'Converts raw graph nodes into living psychological profiles for agent simulation.' }, + { endpoint:'/v1/simulation/tick', label:'Force Simulation Tick', icon:, + desc:'Forces agents to read their local graph memory and output a new interaction edge.' }, + ].map(item => ( +
+ +

{item.desc}

+
+ ))} +
+
+ ); +}; + +// ─── Main Dashboard ─────────────────────────────────────────────────────────── +export default function AdminDashboard() { + const { token, user } = useAuth(); + const [activeTab, setActiveTab] = useState('overview'); + const [stats, setStats] = useState(null); + const [health, setHealth] = useState(null); + const [error, setError] = useState(null); + + const fetchOverview = useCallback(async () => { + if (!token) return; + try { + const [statsRes, healthRes] = await Promise.all([ + fetch(`${API_BASE}/admin/stats`, { headers: { Authorization: `Bearer ${token}` } }), + fetch(`${API_BASE}/system/health`, { headers: { Authorization: `Bearer ${token}` } }), + ]); + if (statsRes.ok) setStats(await statsRes.json()); + if (healthRes.ok) setHealth(await healthRes.json()); + } catch (err: any) { + setError(err.message); + } + }, [token]); + + useEffect(() => { fetchOverview(); }, [fetchOverview]); + + if (user && user.username !== 'admin' && !user.scopes?.includes('admin')) { + return ( +
+ +

Access Denied

+

You need administrative privileges to view this page.

+
+ ); + } + + const TABS = [ + { id:'overview', label:'Overview', icon: }, + { id:'users', label:'Users', icon: }, + { id:'documents', label:'Documents', icon: }, + { id:'graph', label:'Graph CRUD', icon: }, + { id:'ontology', label:'Ontology', icon: }, + { id:'workers', label:'Workers', icon: }, + { id:'enrichment', label:'Enrichment / Drift', icon: }, + { id:'sandbox', label:'God-Mode Sandbox', icon: }, + ]; + + return ( +
+ {/* Header */} +
+
+

Admin Control Center

+

Manage graph data, workers, users, and platform configuration

+
+ {error && ( +
+ {error} +
+ )} +
+ +
+ {/* Sidebar nav */} +
+ +
+ + {/* Main content */} +
+ {activeTab === 'overview' && } + {activeTab === 'users' && } + {activeTab === 'documents' && } + {activeTab === 'graph' && } + {activeTab === 'ontology' && } + {activeTab === 'workers' && } + {activeTab === 'enrichment' && } + {activeTab === 'sandbox' && } +
+
+ + +
+ ); +} diff --git a/frontend-react/src/views/Home.tsx b/frontend-react/src/views/Home.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e072bdae30227ec69ca323e991a12ac1b9b00d38 --- /dev/null +++ b/frontend-react/src/views/Home.tsx @@ -0,0 +1,692 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { useAuth } from '../context/AuthContext'; +import { Network, Server, Cpu, Database, Activity, ArrowRight, Zap, GitBranch, MessageSquare, TrendingUp, RefreshCw } from 'lucide-react'; + +const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api'; + +// Animated counter hook +function useCountUp(target: number, duration = 1200) { + const [val, setVal] = useState(0); + const prev = useRef(0); + useEffect(() => { + if (target === 0) { setVal(0); return; } + const start = prev.current; + const diff = target - start; + const startTime = performance.now(); + const tick = (now: number) => { + const t = Math.min((now - startTime) / duration, 1); + const ease = 1 - Math.pow(1 - t, 3); + setVal(Math.round(start + diff * ease)); + if (t < 1) requestAnimationFrame(tick); + else prev.current = target; + }; + requestAnimationFrame(tick); + }, [target]); + return val; +} + +const StatCounter: React.FC<{ value: number | string; label: string; suffix?: string }> = ({ value, label, suffix = '' }) => { + const numVal = typeof value === 'number' ? value : parseInt(String(value)) || 0; + const animated = useCountUp(numVal); + return ( +
+
{typeof value === 'number' ? animated : value}{suffix}
+
{label}
+
+ ); +}; + +const Home: React.FC = () => { + const { token, logout, user } = useAuth(); + const [health, setHealth] = useState(null); + const [stats, setStats] = useState(null); + const [myStats, setMyStats] = useState(null); + const [loading, setLoading] = useState(true); + const [lastRefresh, setLastRefresh] = useState(new Date()); + + const fetchData = async () => { + setLoading(true); + try { + const [healthRes, statsRes, myStatsRes] = await Promise.all([ + fetch(`${API_BASE}/system/health`), + fetch(`${API_BASE}/system/stats`, { headers: { Authorization: `Bearer ${token}` } }), + fetch(`${API_BASE}/system/my-stats`, { headers: { Authorization: `Bearer ${token}` } }).catch(() => null), + ]); + if (statsRes.status === 401) { logout(); return; } + if (healthRes.ok) setHealth(await healthRes.json()); + if (statsRes.ok) setStats(await statsRes.json()); + if (myStatsRes?.ok) setMyStats(await myStatsRes.json()); + } catch (err) { + console.error('Failed to fetch system data', err); + } finally { + setLoading(false); + setLastRefresh(new Date()); + } + }; + + useEffect(() => { + fetchData(); + const interval = setInterval(fetchData, 30000); + return () => clearInterval(interval); + }, [token]); + + const isOnline = (v: boolean | undefined) => v === true; + + const systemOk = health ? (isOnline(health.neo4j_connected) && isOnline(health.redis_connected)) : false; + + return ( +
+ + {/* ── Hero ─────────────────────────────────────── */} +
+
+
CORTEX PLATFORM
+

+ Agentic Knowledge
+ Intelligence +

+

+ Production-grade knowledge graph · Real-time extraction · Multi-hop reasoning +

+ +
+ +
+
+ + + {loading ? 'CHECKING...' : systemOk ? 'SYSTEM OPERATIONAL' : 'SYSTEM DEGRADED'} + + +
+ +
+ {[ + { label: 'NEO4J', ok: isOnline(health?.neo4j_connected) }, + { label: 'REDIS', ok: isOnline(health?.redis_connected) }, + { label: `${health?.workers_active ?? 0} WORKERS`, ok: true, neutral: true }, + { label: 'API', ok: true }, + ].map(s => ( +
+ + {s.label} + + {s.neutral ? 'ACTIVE' : s.ok ? 'CONNECTED' : 'OFFLINE'} + +
+ ))} +
+
+
+ + {/* ── Platform Metrics ─────────────────────────── */} +
+
PLATFORM METRICS
+
+ +
+ +
+ +
+ +
+
+
+ {stats?.ontology_version ?? '—'} +
+
ONTOLOGY VER
+
+
+
+ + {/* ── Main Grid ────────────────────────────────── */} +
+ + {/* Quick Actions */} +
+
+ QUICK ACTIONS +
+
+ {[ + { href: '/process', icon: , label: 'INGEST DOCUMENTS', desc: 'Upload PDFs, text, or crawl URLs' }, + { href: '/interact', icon: , label: 'QUERY KNOWLEDGE', desc: 'Ask questions across the graph' }, + { href: '/simulate', icon: , label: 'EXPLORE NODES', desc: 'Interactive D3 force visualization' }, + { href: '/ontology', icon: , label: 'MANAGE ONTOLOGY', desc: 'Edit schema & run AI refinement' }, + { href: '/insights', icon: ,label: 'INSIGHTS', desc: 'Quality metrics & AI reports' }, + ].map(a => ( + + {a.icon} +
+
{a.label}
+
{a.desc}
+
+ +
+ ))} +
+
+ + {/* Right column: My Activity + Feature cards */} +
+ + {/* User Activity */} +
+
+ MY ACTIVITY + {user && @{user.username}} +
+
+
+
{myStats?.conversation_count ?? '—'}
+
CONVERSATIONS
+
+
+
{myStats?.message_count ?? '—'}
+
QUERIES SENT
+
+
+
{myStats?.last_active ? new Date(myStats.last_active).toLocaleDateString() : '—'}
+
LAST ACTIVE
+
+
+ {!myStats && ( +
+ Start querying the graph to build your activity history. +
+ )} +
+ + {/* Graph intelligence card */} +
+
+ KNOWLEDGE GRAPH +
+
+ Neo4j-powered semantic knowledge graph. Multi-hop reasoning, entity enrichment, and community detection built in. +
+
+ {['Entities', 'Relationships', 'Communities', 'Graph Export'].map(tag => ( + {tag} + ))} +
+
+
+
+ + {/* ── Feature Showcase ─────────────────────────── */} +
+
PLATFORM CAPABILITIES
+
+ {[ + { + icon: , + title: 'DOCUMENT INGESTION', + desc: 'Ingest PDFs, text files, Markdown, and web URLs. Celery workers extract entities and relationships into the knowledge graph automatically via LLM pipelines.', + color: '#2563eb', + }, + { + icon: , + title: 'GRAPH INTELLIGENCE', + desc: 'Neo4j-powered knowledge graph with rich entity relationships. Query across documents globally or per-source with full ontology control.', + color: '#7c3aed', + }, + { + icon: , + title: 'AGENTIC LOGIC', + desc: 'Multi-step ReACT reasoning agent that searches the graph, retrieves relevant chunks, and streams answers with confidence scoring in real time.', + color: '#059669', + }, + { + icon: , + title: 'LLM-AS-JUDGE', + desc: 'Inline faithfulness evaluation using heuristic scoring. Detects hallucination risk, context precision, and answer quality on every response.', + color: '#d97706', + }, + { + icon: , + title: 'LIVE SIMULATION', + desc: 'Interactive D3 force graph with color-coded entity types, physics controls, fullscreen mode, PNG export, and node detail modals.', + color: '#dc2626', + }, + { + icon: , + title: 'ONTOLOGY DRIFT', + desc: 'Automated schema drift detection that spots when new data no longer fits the current ontology. Propose and approve schema expansions.', + color: '#0891b2', + }, + ].map(f => ( +
+
{f.icon}
+
{f.title}
+
{f.desc}
+
+ ))} +
+
+ + {/* Footer bar */} +
+ CORTEX_PLATFORM + + Last refreshed: {lastRefresh.toLocaleTimeString()} + + + v{health ? '1.0' : '—'} · Neo4j + Redis + Celery + +
+ + +
+ ); +}; + +export default Home; diff --git a/frontend-react/src/views/InsightsView.tsx b/frontend-react/src/views/InsightsView.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bdbdebf952a41640f1d61c0fa7eefca15d6e96cd --- /dev/null +++ b/frontend-react/src/views/InsightsView.tsx @@ -0,0 +1,890 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { useAuth } from '../context/AuthContext'; +import { BarChart2, TrendingUp, AlertTriangle, CheckCircle, RefreshCw, Zap, FileText, GitCommit, MessageSquare } from 'lucide-react'; + +const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api'; + +interface EvalDashboard { + total_evaluations: number; + avg_overall_score: number; + avg_faithfulness: number; + avg_relevancy: number; + hallucination_rate: number; + trend_data: TrendPoint[]; +} + +interface TrendPoint { + timestamp: string; + overall_score: number; + faithfulness: number; + answer_relevancy: number; + hallucination_detected: boolean; + document_id?: string; +} + +interface EvalForm { + question: string; + answer: string; + contexts: string; +} + +// Helper: build markdown from report result +function buildReportMarkdown(result: any, topic: string): string { + const lines: string[] = []; + lines.push(`# ${result.topic || topic}`); + lines.push(''); + if (result.confidence !== undefined) { + lines.push(`**Confidence:** ${(result.confidence * 100).toFixed(1)}%`); + } + if (result.tool_calls_made !== undefined) { + lines.push(`**Tool calls:** ${result.tool_calls_made}`); + } + lines.push(`**Generated:** ${new Date().toLocaleString()}`); + lines.push(''); + + if (result.executive_summary) { + lines.push('## Executive Summary'); + lines.push(''); + lines.push(result.executive_summary); + lines.push(''); + } + + if (result.sections && typeof result.sections === 'object' && !Array.isArray(result.sections)) { + Object.entries(result.sections).forEach(([title, content], i) => { + lines.push(`## ${i + 1}. ${title}`); + lines.push(''); + lines.push(String(content)); + lines.push(''); + }); + } else if (Array.isArray(result.sections)) { + result.sections.forEach((s: any, i: number) => { + lines.push(`## ${i + 1}. ${s.title || ''}`); + lines.push(''); + lines.push(s.content || ''); + lines.push(''); + }); + } else if (!result.sections) { + lines.push(result.report || result.content || result.markdown || JSON.stringify(result, null, 2)); + lines.push(''); + } + + if (result.key_entities && result.key_entities.length > 0) { + lines.push('## Key Entities'); + lines.push(''); + lines.push(result.key_entities.join(', ')); + } + + return lines.join('\n'); +} + +const InsightsView: React.FC = () => { + const { token } = useAuth(); + const [dashboard, setDashboard] = useState(null); + const [loading, setLoading] = useState(false); + const [evalForm, setEvalForm] = useState({ question: '', answer: '', contexts: '' }); + const [evalResult, setEvalResult] = useState(null); + const [evalLoading, setEvalLoading] = useState(false); + const [communities, setCommunities] = useState([]); + const [communityLoading, setCommunityLoading] = useState(false); + const [activeTab, setActiveTab] = useState<'metrics' | 'evaluate' | 'communities' | 'export' | 'report' | 'graph-update' | 'entity-chat'>('metrics'); + + // Report Agent state + const [reportTopic, setReportTopic] = useState(''); + const [reportDepth, setReportDepth] = useState(3); + const [reportResult, setReportResult] = useState(null); + const [reportLoading, setReportLoading] = useState(false); + const [reportTimestamp, setReportTimestamp] = useState(null); + const [copyDone, setCopyDone] = useState(false); + + // Graph Update state + const [updateText, setUpdateText] = useState(''); + const [updateResult, setUpdateResult] = useState(null); + const [updateLoading, setUpdateLoading] = useState(false); + + // Entity Chat state + const [entities, setEntities] = useState([]); + const [selectedEntity, setSelectedEntity] = useState(''); + const [entityContext, setEntityContext] = useState(''); + const [chatMsg, setChatMsg] = useState(''); + const [chatHistory, setChatHistory] = useState<{role: string; content: string}[]>([]); + const [chatLoading, setChatLoading] = useState(false); + + const fetchDashboard = useCallback(async () => { + setLoading(true); + try { + const res = await fetch(`${API_BASE}/eval/dashboard?limit=200`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) setDashboard(await res.json()); + } catch (e) { console.error(e); } + setLoading(false); + }, [token]); + + const fetchCommunities = useCallback(async () => { + setCommunityLoading(true); + try { + const res = await fetch(`${API_BASE}/graph/communities?limit=30`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + const data = await res.json(); + setCommunities(data.communities || []); + } + } catch (e) { console.error(e); } + setCommunityLoading(false); + }, [token]); + + const fetchEntities = useCallback(async () => { + try { + const res = await fetch(`${API_BASE}/admin/graph/nodes?query=&limit=200`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + const data = await res.json(); + setEntities((data.nodes || []).slice(0, 100)); + } + } catch {} + }, [token]); + + useEffect(() => { + fetchDashboard(); + fetchCommunities(); + fetchEntities(); + }, [fetchDashboard, fetchCommunities, fetchEntities]); + + const runEval = async () => { + if (!evalForm.question || !evalForm.answer || !evalForm.contexts) return; + setEvalLoading(true); + setEvalResult(null); + try { + const res = await fetch(`${API_BASE}/eval/score`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, + body: JSON.stringify({ + question: evalForm.question, + answer: evalForm.answer, + contexts: evalForm.contexts.split('\n---\n').map(c => c.trim()).filter(Boolean) + }) + }); + if (res.ok) { + setEvalResult(await res.json()); + fetchDashboard(); // Refresh metrics + } + } catch (e) { console.error(e); } + setEvalLoading(false); + }; + + const runReport = async () => { + if (!reportTopic.trim()) return; + setReportLoading(true); setReportResult(null); + try { + const res = await fetch(`${API_BASE}/report`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, + body: JSON.stringify({ topic: reportTopic, depth: reportDepth }) + }); + if (res.ok) { + setReportResult(await res.json()); + setReportTimestamp(new Date()); + } + } catch (e) { console.error(e); } + setReportLoading(false); + }; + + const runGraphUpdate = async () => { + if (!updateText.trim()) return; + setUpdateLoading(true); setUpdateResult(null); + try { + const res = await fetch(`${API_BASE}/graph/update`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, + body: JSON.stringify({ text: updateText }) + }); + if (res.ok) setUpdateResult(await res.json()); + } catch (e) { console.error(e); } + setUpdateLoading(false); + }; + + const sendEntityChat = async () => { + if (!selectedEntity || !chatMsg.trim()) return; + const userMsg = { role: 'user', content: chatMsg }; + setChatHistory(h => [...h, userMsg]); + setChatMsg(''); + setChatLoading(true); + try { + const res = await fetch(`${API_BASE}/entities/${encodeURIComponent(selectedEntity)}/chat`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, + body: JSON.stringify({ message: userMsg.content }) + }); + if (res.ok) { + const data = await res.json(); + setChatHistory(h => [...h, { role: 'assistant', content: data.response || data.answer || JSON.stringify(data) }]); + } + } catch (e) { console.error(e); } + setChatLoading(false); + }; + + const assignCommunities = async () => { + setCommunityLoading(true); + try { + const res = await fetch(`${API_BASE}/graph/communities/assign`, { + method: 'POST', + headers: { Authorization: `Bearer ${token}` } + }); + if (res.ok) { + const data = await res.json(); + alert(`✓ ${data.message}`); + fetchCommunities(); + } + } catch (e) { console.error(e); } + setCommunityLoading(false); + }; + + const exportGraph = async (fmt: string) => { + const res = await fetch(`${API_BASE}/graph/export?format=${fmt}`, { + headers: { Authorization: `Bearer ${token}` } + }); + if (!res.ok) return; + const blob = await res.blob(); + const ext = fmt === 'json' ? 'json' : fmt === 'cypher' ? 'cypher' : 'graphml'; + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; a.download = `graph_export.${ext}`; a.click(); + URL.revokeObjectURL(url); + }; + + const handleCopyReport = () => { + if (!reportResult) return; + const text = buildReportMarkdown(reportResult, reportTopic); + navigator.clipboard.writeText(text).then(() => { + setCopyDone(true); + setTimeout(() => setCopyDone(false), 2000); + }); + }; + + const handleDownloadReport = () => { + if (!reportResult) return; + const text = buildReportMarkdown(reportResult, reportTopic); + const blob = new Blob([text], { type: 'text/markdown' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `report_${reportTopic.slice(0, 30).replace(/\s+/g, '_')}.md`; + a.click(); + URL.revokeObjectURL(url); + }; + + const score2color = (s: number) => { + if (s >= 0.8) return '#16a34a'; + if (s >= 0.6) return '#d97706'; + return '#dc2626'; + }; + const score2label = (s: number) => s >= 0.8 ? 'HIGH' : s >= 0.6 ? 'MEDIUM' : 'LOW'; + + const ScoreBar: React.FC<{ label: string; value: number }> = ({ label, value }) => ( +
+
+ {label} + {(value * 100).toFixed(1)}% +
+
+
+
+
+ ); + + const confidenceBadgeColor = (c: number) => + c >= 0.8 ? '#16a34a' : c >= 0.6 ? '#d97706' : '#dc2626'; + + return ( +
+
+
+

INSIGHTS HQ

+

QUALITY METRICS // EVAL DASHBOARD // GRAPH INTELLIGENCE

+
+
+ +
+
+ + {/* Tab Nav */} +
+ {([ + { id: 'metrics', label: 'METRICS' }, + { id: 'evaluate', label: 'EVALUATE' }, + { id: 'communities', label: 'COMMUNITIES' }, + { id: 'export', label: 'EXPORT' }, + { id: 'report', label: '⚡ REPORT' }, + { id: 'graph-update', label: '↑ LIVE UPDATE' }, + { id: 'entity-chat', label: '💬 ENTITY CHAT' }, + ] as const).map(t => ( + + ))} +
+ + {/* ── METRICS TAB ── */} + {activeTab === 'metrics' && ( +
+ {loading ? ( +
LOADING METRICS...
+ ) : !dashboard || dashboard.total_evaluations === 0 ? ( +
+ +

NO EVALUATION DATA YET

+

+ Use the EVALUATE tab to score Q&A pairs and build your quality history. +

+
+ ) : ( + <> + {/* KPI Cards */} +
+ {[ + { label: 'TOTAL EVALS', value: dashboard.total_evaluations, raw: true, icon: }, + { label: 'AVG QUALITY', value: dashboard.avg_overall_score, icon: }, + { label: 'FAITHFULNESS', value: dashboard.avg_faithfulness, icon: }, + { label: 'HALLUCINATION RATE', value: dashboard.hallucination_rate, invert: true, icon: }, + ].map(card => ( +
0.3 : false) ? '#fff5f5' : '#fff' }}> +
+ {card.label} + {card.icon} +
+
+ {card.raw ? card.value : `${((card.value as number) * 100).toFixed(1)}%`} +
+
+ ))} +
+ + {/* Score Breakdown */} +
+

METRIC BREAKDOWN

+ + + + +
+ + {/* Trend Table */} + {dashboard.trend_data.length > 0 && ( +
+

EVALUATION HISTORY (LATEST {Math.min(dashboard.trend_data.length, 20)})

+
+ + + + {['TIMESTAMP', 'QUALITY', 'FAITHFULNESS', 'RELEVANCY', 'HALLUCINATION'].map(h => ( + + ))} + + + + {dashboard.trend_data.slice(0, 20).map((p, i) => ( + + + + + + + + ))} + +
{h}
{p.timestamp}{(p.overall_score * 100).toFixed(1)}%{(p.faithfulness * 100).toFixed(1)}%{(p.answer_relevancy * 100).toFixed(1)}% + + {p.hallucination_detected ? 'YES' : 'NO'} + +
+
+
+ )} + + )} +
+ )} + + {/* ── EVALUATE TAB ── */} + {activeTab === 'evaluate' && ( +
+
+

+ + SCORE A Q&A PAIR +

+

+ Paste a question, its generated answer, and the retrieved context chunks (separate chunks with "---"). +

+ +
+
+ +