diff --git a/.github/workflows/main-wf.yaml b/.github/workflows/main-wf.yaml index 2f384fb5..2771050a 100644 --- a/.github/workflows/main-wf.yaml +++ b/.github/workflows/main-wf.yaml @@ -18,7 +18,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: NPM install - run: npm --prefix src/frontend/static/frontend i + run: npm --prefix src/frontend/static/frontend i --legacy-peer-deps - name: NPM linter checks run: npm --prefix src/frontend/static/frontend run check-all docker-multiomix: diff --git a/.github/workflows/pr-wf.yaml b/.github/workflows/pr-wf.yaml index 958b6f54..85dafe6b 100644 --- a/.github/workflows/pr-wf.yaml +++ b/.github/workflows/pr-wf.yaml @@ -15,6 +15,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: NPM install - run: npm --prefix src/frontend/static/frontend i + run: npm --prefix src/frontend/static/frontend i --legacy-peer-deps - name: NPM linter checks run: npm --prefix src/frontend/static/frontend run check-all diff --git a/.gitignore b/.gitignore index 66e1db9b..f1352fde 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pyc .idea .vscode +!src/frontend/static/frontend/.vscode src/frontend/static/frontend/node_modules src/frontend/static/frontend/dist src/venv @@ -12,4 +13,5 @@ src/api_service/experiments/venv docker-compose.yml src/secretkey.txt docker-compose.mauri_dev.yml -venv \ No newline at end of file +venv +.DS_Store \ No newline at end of file diff --git a/.sonarlint/connectedMode.json b/.sonarlint/connectedMode.json new file mode 100644 index 00000000..1644a276 --- /dev/null +++ b/.sonarlint/connectedMode.json @@ -0,0 +1,5 @@ +{ + "sonarCloudOrganization": "omics-datascience", + "projectKey": "omics-datascience_multiomix", + "region": "EU" +} \ No newline at end of file diff --git a/AI_ASSISTANT.md b/AI_ASSISTANT.md new file mode 100644 index 00000000..c84ede71 --- /dev/null +++ b/AI_ASSISTANT.md @@ -0,0 +1,644 @@ +# Multiomix AI Assistant + +Technical documentation for the AI assistant integrated into the Multiomix platform. + +--- + +## Table of contents + +1. [Overview](#1-overview) +2. [Architecture](#2-architecture) +3. [Backend — Django app `assistant`](#3-backend--django-app-assistant) + - [Models](#models) + - [REST API](#rest-api) + - [Embedding service](#embedding-service) + - [LLM service and agent](#llm-service-and-agent) + - [Cross-chat semantic memory](#cross-chat-semantic-memory) +4. [Available tools](#4-available-tools) + - [User data (Multiomix)](#user-data-multiomix) + - [Biological information (BioAPI / Modulector)](#biological-information-bioapi--modulector) + - [STRING database](#string-database) + - [Curated knowledge base](#curated-knowledge-base) + - [External MCP tools (BioMCP)](#external-mcp-tools-biomcp) +5. [Frontend — React widget](#5-frontend--react-widget) +6. [Full query flow](#6-full-query-flow) +7. [Environment variables and configuration](#7-environment-variables-and-configuration) +8. [How to change the LLM model](#8-how-to-change-the-llm-model) +9. [How to add a new tool](#9-how-to-add-a-new-tool) +10. [How to add an MCP server](#10-how-to-add-an-mcp-server) +11. [How to add curated documentation](#11-how-to-add-curated-documentation) +12. [Dependencies](#12-dependencies) + +--- + +## 1. Overview + +The assistant is an **LLM agent with tool-calling** embedded in Multiomix as a floating widget visible on every page of the platform (authenticated users only). It allows users to query in natural language: + +- Their own experiments, results, and biomarkers +- Biological information about genes, miRNAs, and drugs +- Protein interaction networks and functional enrichment (STRING) +- Biomedical literature: PubMed papers, preprints (bioRxiv/medRxiv), clinical trials (ClinicalTrials.gov) +- Genomic variants and gene/drug/disease annotations from curated databases +- Platform documentation and general concepts + +The assistant **never fabricates data**. It always retrieves real information via tools connected to the Multiomix database and external APIs. + +--- + +## 2. Architecture + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ FRONTEND (React) │ +│ │ +│ ChatWidget (FAB) → ChatPanel → ConversationList + MessageThread│ +│ ↕ localStorage (open state, active conv ID) │ +└────────────────────────┬────────────────────────────────────────────┘ + │ HTTP (ky, 3 min timeout) + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ DJANGO — app: assistant │ +│ │ +│ POST /assistant/api/chat/ → ChatView (sync, DRF) │ +│ GET /assistant/api/conversations/ → ConversationListView │ +│ GET/DELETE /assistant/api/conversations// → ConversationDetailView│ +└────────────────────────┬────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ llm_service.run_chat() [sync] │ +│ │ +│ 1. EmbeddingService.embed(user_message) ← HuggingFace local │ +│ 2. build_chat_history() ← PostgreSQL + pgvector │ +│ ├─ 15 most recent messages (current conversation) │ +│ └─ 5 semantically similar messages (ALL user conversations — │ +│ cross-chat memory) │ +│ 3. load_mcp_config() ← config/mcp_servers.json│ +│ 4. asyncio.run(_async_agent()) ← LangChain 1.x + OpenAI │ +│ ├─ MultiServerMCPClient(mcp_config) ← MCP subprocess (stdio) │ +│ │ └─ client.get_tools() ← BioMCP tools │ +│ ├─ all_tools = internal (23) + mcp │ +│ └─ create_agent(llm, all_tools, prompt=SYSTEM_PROMPT) │ +│ 5. Persist user msg + assistant reply with embeddings │ +└──────────┬──────────────────────────────────────────────────────────┘ + │ tool calls + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ TOOLS │ +│ │ +│ Internal (23): │ +│ ├─ PostgreSQL (Django ORM) → experiments, biomarkers, files │ +│ ├─ MongoDB → experiment results │ +│ ├─ Modulector API → miRNA-gene interactions │ +│ ├─ BioAPI → gene annotations, drugs │ +│ └─ STRING REST API → protein interactions, enrichment │ +│ │ +│ External MCP (BioMCP, optional): │ +│ ├─ PubMed / bioRxiv / medRxiv → biomedical papers │ +│ ├─ ClinicalTrials.gov → clinical trials │ +│ ├─ NCI / OncoKB → variants, oncology annotations │ +│ └─ cBioPortal → genomic studies │ +└─────────────────────────────────────────────────────────────────────┘ + │ embeddings + ▼ +┌────────────────────────────┐ +│ PostgreSQL + pgvector │ +│ Conversation, Message, │ +│ CuratedDocument │ +│ (VectorField 384 dims) │ +└────────────────────────────┘ +``` + +--- + +## 3. Backend — Django app `assistant` + +### Models + +Location: `src/assistant/models.py` + +#### `Conversation` +Groups the messages of a chat session. + +| Field | Type | Description | +|-------|------|-------------| +| `user` | FK → User | Conversation owner | +| `title` | CharField | First 100 characters of the first message (auto-generated) | +| `created_at` | DateTimeField | Creation date | +| `updated_at` | DateTimeField | Updated on every turn | + +#### `Message` +An individual message (user or assistant) within a conversation. + +| Field | Type | Description | +|-------|------|-------------| +| `conversation` | FK → Conversation | Conversation this message belongs to | +| `role` | CharField | `user` or `assistant` | +| `content` | TextField | Message content | +| `embedding` | VectorField(384) | Semantic embedding for similarity search | +| `created_at` | DateTimeField | Timestamp | + +#### `CuratedDocument` +Manual documentation about Multiomix that the agent can query via semantic search. + +| Field | Type | Description | +|-------|------|-------------| +| `title` | CharField | Document title | +| `content` | TextField | Content (free text / markdown) | +| `category` | CharField | Category (e.g. `"platform"`, `"biology"`) | +| `embedding` | VectorField(384) | Auto-generated on save from the admin | +| `is_active` | BooleanField | Whether the document is active for search | + +> Curated documents are managed from the **Django Admin** at `/admin/`. The embedding is generated automatically on save. + +--- + +### REST API + +All endpoints require authentication (`IsAuthenticated`). + +| Method | URL | Description | +|--------|-----|-------------| +| `POST` | `/assistant/api/chat/` | Send a message and receive a response | +| `GET` | `/assistant/api/conversations/` | List the user's conversations | +| `GET` | `/assistant/api/conversations//` | Conversation detail with all its messages | +| `DELETE` | `/assistant/api/conversations//` | Delete a conversation | + +**POST `/assistant/api/chat/`** + +Request: +```json +{ + "message": "What are my correlation experiments?", + "conversation_id": 42 // optional; omit to start a new conversation +} +``` + +Response: +```json +{ + "conversation_id": 42, + "reply": "You have **3 correlation experiments**..." +} +``` + +--- + +### Embedding service + +Location: `src/assistant/services/embedding_service.py` + +- Model: `sentence-transformers/all-MiniLM-L6-v2` (384 dimensions) +- **100% local inference** — no text is sent to any external server +- Implemented as a **lazy-loading singleton**: the model is loaded into memory the first time it is needed and then reused +- Configurable via `ASSISTANT_EMBEDDING_MODEL` and `ASSISTANT_EMBEDDING_DIMENSIONS` + +```python +from assistant.services.embedding_service import embedding_service + +vector = embedding_service.embed("TP53 expression in breast cancer") +# → List[float] with 384 values +``` + +> If the model is not in the local cache (`~/.cache/huggingface/`), it is downloaded automatically on first startup. + +--- + +### LLM service and agent + +Location: `src/assistant/services/llm_service.py` + +The agent uses the **langchain 1.x** API backed by LangGraph: + +```python +agent = create_agent(llm, all_tools, prompt=SYSTEM_PROMPT) +result = await agent.ainvoke({"messages": messages}) +``` + +`run_chat()` is a **sync** function (called from a standard WSGI/DRF view). Because `MultiServerMCPClient` requires an async context, the agent execution is isolated in `_async_agent()` and run via `asyncio.run()`. Sync DB operations (embed, save messages) stay outside the event loop. + +``` +run_chat() [sync] + ├─ DB ops (embed, build history, save user message) + ├─ load_mcp_config() + └─ asyncio.run(_async_agent(...)) + ├─ MultiServerMCPClient ← spawns MCP subprocesses + ├─ all_tools = internal + mcp + ├─ create_agent(llm, all_tools, prompt=SYSTEM_PROMPT) + └─ agent.ainvoke({"messages": [*history, HumanMessage]}) + └─ DB ops (embed, save assistant reply) +``` + +**Graceful fallback**: if any MCP server fails to start or crashes, a `WARNING` is logged and the agent continues with the 23 internal tools only — the assistant remains fully functional. + +**System prompt restrictions**: the agent only answers questions about bioinformatics, the Multiomix platform, the user's own data, and biomedical literature. Out-of-scope questions receive a fixed rejection message. + +**`get_llm()` function**: the model swap point. See section [8](#8-how-to-change-the-llm-model). + +--- + +### Cross-chat semantic memory + +The context history the agent receives on each turn is built as follows: + +``` +build_chat_history(conversation, user_id, query_embedding) +``` + +| Source | Count | Filter | +|--------|-------|--------| +| Recent messages | Last 15 | Current conversation only | +| Semantic messages | Top 5 by cosine similarity | **All user conversations** | + +Cross-chat semantic search allows the assistant to recall information mentioned in previous chats (e.g. the user's name, context from an experiment) without having to repeat it. + +Configurable via `ASSISTANT_RECENT_MESSAGES_COUNT` and `ASSISTANT_SEMANTIC_MESSAGES_COUNT`. + +--- + +## 4. Available tools + +Location: `src/assistant/services/tools.py` + +Tools are built in `make_tools(user_id)`. The `user_id` is injected server-side via closure — **the LLM never controls which user is queried**. + +### User data (Multiomix) + +| Tool | Description | +|------|-------------| +| `get_user_experiments` | Lists correlation experiments (name, status, type, date) | +| `get_experiment_top_results` | Top results of a correlation experiment (gene, GEM, correlation, p-value) | +| `get_experiment_detail` | Full configuration of an experiment: datasets used, correlation method, thresholds, and execution statistics | +| `get_user_biomarkers` | The user's own and public biomarkers | +| `get_genes_in_biomarker` | All identifiers in a biomarker grouped by type: mRNAs, miRNAs, CNAs, and methylations | +| `get_statistical_validations` | Statistical validations of biomarkers (c-index, MSE) | +| `get_survival_experiments` | Lists statistical survival validation experiments with metrics (c_index, cox_c_index, cox_log_likelihood, r2_score, MSE) | +| `get_survival_results` | Validation detail: full survival metrics + molecules with Cox coefficients (positive coefficient = higher risk, negative = protective) | +| `get_inference_experiments` | ML inference experiments | +| `get_feature_selection_experiments` | Feature selection experiments | +| `get_user_files` | Files uploaded by the user (name, type, samples, date) | +| `get_differential_expression_experiments` | Lists DE experiments (tool, status, date) | +| `get_differential_expression_results` | Top DE genes from an experiment (logFC, FDR, p-value) | +| `find_gene_across_experiments` | Searches for a gene across all the user's correlation results (miRNA, CNA, Methylation) and returns which experiments it appears in along with its paired GEM and statistics | +| `search_cgds_studies` | Search public cBioPortal studies by name | + +### Biological information (BioAPI / Modulector) + +| Tool | Source | Description | +|------|--------|-------------| +| `get_gene_info` | Local PostgreSQL | Basic gene info (type, chromosome, position) | +| `get_gene_annotations` | BioAPI | Detailed annotations (aliases, biotype, Ensembl, NCBI, HGNC) | +| `get_mirna_modulators` | Modulector | miRNAs that regulate the expression of a gene | +| `get_drugs_regulating_gene` | BioAPI | Link to DrugBank with drugs that modulate gene expression | + +### STRING database + +Integration with the public [STRING REST API](https://string-db.org/help/api/) (no API key required, `species=9606` — human). + +| Tool | Description | +|------|-------------| +| `get_string_interaction_partners(gene_name, limit=10)` | Top protein interactors with scores (combined, experimental, textmining, databases, coexpression) | +| `get_string_functional_enrichment(gene_names)` | Functional enrichment: GO BP/MF/CC, KEGG, Reactome. `gene_names` is comma-separated. Returns top 15 terms by FDR | +| `get_string_network_url(gene_names)` | URL of a PNG image of the interaction network. The agent embeds it in its response as `![STRING Network](url)`, which renders directly in the chat | + +**Example usage in the chat:** + +> *"Show the interaction network for TP53, BRCA1, and MYC"* +> +> → The agent calls `get_string_network_url("TP53,BRCA1,MYC")` and responds with the image embedded in markdown. + +> *"Which pathways are enriched in the genes from my experiment #5?"* +> +> → The agent calls `get_experiment_top_results(5)` to retrieve the genes, then `get_string_functional_enrichment("GENE1,GENE2,...")` for enrichment. + +### Curated knowledge base + +| Tool | Description | +|------|-------------| +| `search_curated_knowledge(query)` | Semantic search over `CuratedDocument`. Returns the top 5 documents by cosine similarity. Useful for answering questions about how the platform works | + +### External MCP tools (BioMCP) + +Provided by the `biomcp-python` package via the MCP protocol (stdio subprocess). Available when `ASSISTANT_MCP_CONFIG_PATH` is set and `biomcp` is installed. + +| Category | Examples | +|----------|---------| +| PubMed / bioRxiv / medRxiv | Search papers by gene, disease, method, author | +| ClinicalTrials.gov | Search clinical trials by condition or intervention | +| NCI / OncoKB | Variant annotations, oncogenicity classifications | +| cBioPortal | Studies, mutations, copy number alterations | + +**Example usage in the chat:** + +> *"Find recent PubMed papers about DESeq2 differential expression in breast cancer"* + +> *"Are there clinical trials for BRCA1 mutations in ovarian cancer?"* + +The tools are discovered automatically from the running MCP server — no code changes are needed when BioMCP adds new tools in a future release. + +--- + +## 5. Frontend — React widget + +Location: `src/frontend/static/frontend/src/components/assistant/` + +### Components + +``` +ChatWidget.tsx — FAB (floating action button) + controls panel visibility +ChatPanel.tsx — Main panel: header, conversation list, message thread +ConversationList.tsx — Left sidebar with conversation history +MessageThread.tsx — Message area + input +types.ts — TypeScript interfaces +``` + +### Styles + +`src/frontend/static/frontend/src/css/chat-widget.css` + +### localStorage persistence + +| Key | Value | Description | +|-----|-------|-------------| +| `multiomix_chat_open` | `"1"` / `"0"` | Whether the panel is open | +| `multiomix_chat_conv_id` | Numeric ID | Active conversation when navigating between pages | + +When `ChatPanel` mounts, it verifies that the stored conversation still exists on the server. If it has been deleted, the storage is cleared and a new conversation is started. + +### Markdown rendering + +Assistant messages are rendered with `react-markdown` + `remark-gfm`: +- Tables (enrichment results, gene lists) +- Inline code and code blocks +- Lists and bold text +- **Images** (`![alt](url)`) — used for STRING networks + +User messages are displayed as plain text with `white-space: pre-wrap`. + +### Layout integration + +In `Base.tsx`, the widget is rendered only for authenticated non-anonymous users: + +```tsx +{currentUser && !currentUser.is_anonymous && } +``` + +The API URLs are passed as global JS variables from `base.html`: +```html + +``` + +--- + +## 6. Full query flow + +``` +User types → Enter + │ + ▼ +ChatPanel.sendMessage() + ├─ Optimistically adds message to the UI (role: user) + └─ POST /assistant/api/chat/ { message, conversation_id } + │ (timeout: 3 min) + ▼ + ChatView.post() + ├─ Gets/creates Conversation + └─ llm_service.run_chat(conversation, message, user_id) + │ + ├─ 1. EmbeddingService.embed(message) → 384D vector + │ + ├─ 2. build_chat_history() + │ ├─ 15 recent msgs (current conv) + │ └─ 5 semantic msgs (all user convs) + │ + ├─ 3. Message.objects.create(role=user, embedding=...) + │ + ├─ 4. load_mcp_config() ← reads mcp_servers.json + │ + ├─ 5. asyncio.run(_async_agent(...)) + │ ├─ MultiServerMCPClient → spawns BioMCP subprocess + │ ├─ all_tools = 23 internal + MCP tools + │ ├─ create_agent(llm, all_tools, system_prompt) + │ ├─ agent.ainvoke({messages: [history + user_msg]}) + │ │ ├─ LLM decides which tools to call + │ │ ├─ Executes tools (DB, external APIs, MCP) + │ │ └─ LLM generates final response in markdown + │ └─ [fallback to 23 internal tools if MCP fails] + │ + └─ 6. Message.objects.create(role=assistant, embedding=...) + │ + ▼ + Response { conversation_id, reply } + │ + ▼ +ChatPanel receives response + ├─ Appends assistant message (rendered with ReactMarkdown) + ├─ Saves conversation_id to localStorage + └─ Reloads conversation list +``` + +--- + +## 7. Environment variables and configuration + +In `settings.py`: + +| Variable | Default | Description | +|----------|---------|-------------| +| `OPENAI_API_KEY` | `""` | OpenAI API key (required with the default LLM) | +| `ASSISTANT_LLM_MODEL` | `"gpt-4o-mini"` | LLM model to use | +| `ASSISTANT_LLM_TEMPERATURE` | `0.0` | Temperature (0 = deterministic) | +| `ASSISTANT_EMBEDDING_MODEL` | `"sentence-transformers/all-MiniLM-L6-v2"` | Embedding model (HuggingFace) | +| `ASSISTANT_EMBEDDING_DIMENSIONS` | `384` | Vector dimensions (must match the model) | +| `ASSISTANT_RECENT_MESSAGES_COUNT` | `15` | Recent messages in context | +| `ASSISTANT_SEMANTIC_MESSAGES_COUNT` | `5` | Cross-chat semantic messages in context | + +Optional variables: + +| Variable | Description | +|----------|-------------| +| `HF_TOKEN` | HuggingFace token (only required if the model needs authentication) | +| `HF_CACHE_DIR` | Cache directory for HuggingFace models | +| `ASSISTANT_MCP_CONFIG_PATH` | Absolute path to `mcp_servers.json`. If not set or the file is missing, MCP tools are disabled and the assistant uses only the 23 internal tools | + +--- + +## 8. How to change the LLM model + +The `get_llm()` function in `src/assistant/services/llm_service.py` is the only point to modify: + +### OpenAI (default) +```python +def get_llm(): + from langchain_openai import ChatOpenAI + return ChatOpenAI( + model=settings.ASSISTANT_LLM_MODEL, # "gpt-4o-mini", "gpt-4o", etc. + temperature=settings.ASSISTANT_LLM_TEMPERATURE, + api_key=settings.OPENAI_API_KEY, + ) +``` + +### Anthropic Claude +```python +def get_llm(): + from langchain_anthropic import ChatAnthropic + return ChatAnthropic( + model=settings.ASSISTANT_LLM_MODEL, # "claude-sonnet-4-6" + temperature=settings.ASSISTANT_LLM_TEMPERATURE, + api_key=settings.ANTHROPIC_API_KEY, + ) +``` +Dep: `pip install langchain-anthropic` + +### Ollama (local, no cost) +```python +def get_llm(): + from langchain_ollama import ChatOllama + return ChatOllama( + model=settings.ASSISTANT_LLM_MODEL, # "llama3.1:8b", "qwen2.5:7b" + temperature=settings.ASSISTANT_LLM_TEMPERATURE, + ) +``` +Dep: `pip install langchain-ollama` + Ollama running locally. + +> **Note**: the model must support **tool/function calling** for the agent to work correctly. + +--- + +## 9. How to add a new tool + +1. Open `src/assistant/services/tools.py` +2. Add the function decorated with `@tool` inside `make_tools(user_id)` +3. Include it in the `return [...]` at the end of the function + +```python +@tool +def get_my_new_tool(param: str) -> str: + """ + Clear description of what this tool does and WHEN the LLM should use it. + This docstring is what the LLM reads to decide whether to call the tool. + """ + # Security: user_id always comes from the closure, never from the LLM + from myapp.models import MyModel + qs = MyModel.objects.filter(user_id=user_id, name__icontains=param) + return json.dumps(list(qs.values(...)), default=str) +``` + +**Important rules:** +- `user_id` must always come from the closure — never as a tool parameter +- The docstring is critical: the LLM uses it to decide when to call the tool +- Always return a JSON-serializable string +- Handle exceptions and return `{'error': '...'}` on failure + +--- + +## 10. How to add an MCP server + +Adding a new MCP server requires **only editing `config/mcp_servers.json`** — no code changes needed. + +```json +{ + "version": "1.0", + "servers": { + "biomcp": { + "enabled": true, + "transport": "stdio", + "command": "biomcp", + "args": ["run"] + }, + "my-new-server": { + "enabled": true, + "description": "Optional description for humans", + "transport": "stdio", + "command": "my-mcp-command", + "args": ["--flag", "value"], + "env": { + "MY_API_KEY": "secret" + } + } + } +} +``` + +**Schema reference:** + +| Field | Required | Description | +|-------|----------|-------------| +| `enabled` | No (default `true`) | Set to `false` to disable without removing the entry | +| `transport` | Yes | `"stdio"`, `"http"`, or `"sse"` | +| `command` | Yes (stdio) | Executable name or path. Must be in `PATH` | +| `args` | No | List of arguments passed to the command | +| `env` | No | Extra environment variables injected into the subprocess | +| `url` | Yes (http/sse) | Server URL for HTTP or SSE transports | + +**The new server's tools are discovered automatically** at every request — the LLM receives their descriptions and decides when to call them. + +**When to also update the system prompt:** if the new server covers a domain not listed in `## Your scope` (e.g. pharmacogenomics), add a bullet point to `SYSTEM_PROMPT` in `llm_service.py` so the agent knows it is allowed to answer questions in that domain. + +**Validation rules** (applied by `mcp_loader.py` at load time): +- File missing or invalid JSON → warning logged, MCP disabled (internal tools still work) +- `enabled: false` → silently skipped +- Unknown transport → warning + skip +- `command` not found in PATH (stdio) → warning + skip + +--- + +## 11. How to add curated documentation + +`CuratedDocument` entries allow adding platform-specific knowledge that the agent can query semantically. + +1. Go to Django Admin: `/admin/assistant/curateddocument/add/` +2. Fill in the title, content, and category +3. Save — the embedding is generated automatically + +**Examples of useful documents:** +- "How to interpret the C-index in statistical validations" +- "Differences between DESeq2 and limma in differential expression analysis" +- "How to upload methylation files with CpG site IDs" + +--- + +## 12. Dependencies + +### Backend (`config/requirements.txt`) + +``` +langchain==1.3.2 +langchain-openai==1.2.2 +langchain-community==0.4.2 +langchain-huggingface==1.2.2 +langchain-mcp-adapters==0.2.2 +pgvector==0.4.2 +sentence-transformers==3.0.0 +openai==2.38.0 +biomcp-python==0.7.3 +``` + +**Note on langchain 1.x:** the entire langchain stack was upgraded from `0.3.x` to `1.x` because `langchain-mcp-adapters 0.2.x` requires `langchain-core>=1.4.0`, which is incompatible with the `0.3.x` series. As part of this upgrade, the agent API changed: `AgentExecutor` + `create_tool_calling_agent` were replaced by the single `create_agent()` function backed by LangGraph. + +### Frontend (`package.json`) + +``` +react-markdown +remark-gfm +``` + +### Database + +Requires **PostgreSQL with the pgvector extension**. In development, use the Docker image: + +```yaml +# docker-compose.dev.yml +image: pgvector/pgvector:pg16 +``` + +The extension is created automatically in the `assistant` app's initial migration: + +```python +# src/assistant/migrations/0001_initial.py +migrations.RunSQL("CREATE EXTENSION IF NOT EXISTS vector;") +``` diff --git a/DEPLOYING.md b/DEPLOYING.md index 8f700089..29cbcde0 100644 --- a/DEPLOYING.md +++ b/DEPLOYING.md @@ -37,6 +37,7 @@ The following are the steps to perform a deployment in production. In case you w - `SYNC_STUDY_SOFT_TIME_LIMIT`: Time limit in seconds for a CGDSStudy to be synchronized. If It's not finished in this time, it is marked as `TIMEOUT_EXCEEDED`. Default to `3600` (1 hour). - `RESULT_DATAFRAME_LIMIT_ROWS`: maximum number of tuples of an experiment result to save in DB. If it has a larger amount it is truncated by warning the user. The bigger the size the longer it takes to save the resulting combinations of a correlation analysis in Postgres. Set it to `0` to save all the resulting combinations. Default to `300000`. - `EXPERIMENT_CHUNK_SIZE`: the size of the batches/chunks in which each dataset of an experiment is processed. By default, `500`. + - `INSERT_CHUNK_SIZE`: number of rows per INSERT statement when saving experiment results to PostgreSQL. Increase for faster writes; decrease if you hit memory limits. Default `1000`. - `SORT_BUFFER_SIZE`: number of elements in memory to perform external sorting (i.e. disk sorting) in the case of having to sort by fit. This impacts the final sorting performance during the computation of an experiment, at the cost of higher memory consumption. Default `2_000_000` of elements. - `NUMBER_OF_LAST_EXPERIMENTS`: number of last experiments shown to each user in the `Last experiments` panel in the `Pipeline` page. Default `4`. - `MAX_NUMBER_OF_OPEN_TABS`: maximum number of experiment result tabs that the user can open. When the limit is reached it throws a prompt asking to close some tabs to open more. The more experiment tabs you open, the more memory is consumed. Default `8`. @@ -97,6 +98,18 @@ The following are the steps to perform a deployment in production. In case you w - `AWS_EMR_PORT`: AWS-EMR integration service connection port. Default `8003`. - `AWS_EMR_SHARED_FOLDER_DATA`: Share folder with the AWS-EMR integration service to move the datasets to be consumed by the integration service. Default `/data-spark`. - `AWS_EMR_SHARED_FOLDER_RESULTS`: Share folder with the AWS-EMR integration service to retrieve the results generated by the integration service. Default `/results-spark`. + - AI Assistant: + - `OPENAI_API_KEY`: API key for the OpenAI-compatible LLM provider used by the assistant. **Required** for the assistant to function. + - `ASSISTANT_LLM_MODEL`: LLM model name to use for chat completions. Default `gpt-5.4-mini`. + - `ASSISTANT_LLM_TEMPERATURE`: Sampling temperature for the LLM (0.0 = deterministic). Default `0.0`. + - `ASSISTANT_EMBEDDING_MODEL`: HuggingFace sentence-transformers model used to embed messages for semantic memory. Default `sentence-transformers/all-MiniLM-L6-v2`. Inference runs **locally** — no text is sent to HuggingFace. + - `ASSISTANT_EMBEDDING_DIMENSIONS`: Dimensionality of the embedding vectors produced by `ASSISTANT_EMBEDDING_MODEL`. Must match the chosen model. Default `384`. + - `ASSISTANT_RECENT_MESSAGES_COUNT`: Number of most-recent messages from the current conversation included in each LLM prompt. Default `15`. + - `ASSISTANT_SEMANTIC_MESSAGES_COUNT`: Number of semantically similar messages retrieved from the user's conversation history (cross-chat memory) and added to the prompt. Default `5`. + - `HF_TOKEN`: HuggingFace API token. When provided, the embedding model is downloaded from the Hub on first use. When omitted, the model must already be cached locally (fully offline mode). Default empty (offline). + - `HF_CACHE_DIR`: Directory where HuggingFace models are stored when downloaded. Overrides the default HuggingFace cache location. Default empty (uses the HuggingFace default). + - `HF_HOME`: Root directory of the local HuggingFace cache, used to detect whether a model is already cached. Default `~/.cache/huggingface`. + - `TOKENIZERS_PARALLELISM`: Controls HuggingFace tokenizers parallelism. Set to `false` to avoid deadlocks when running inside forked Celery workers. Default `false`. - Redis server for WebSocket connections: - `REDIS_HOST`: IP of the Redis server, if Docker is used it should be the name of the service since Docker has its own DNS and can resolve it. The default is `redis` which is the name of the service. - `REDIS_PORT`: Redis server port. Default `6379`. @@ -218,6 +231,86 @@ Then the following environment variables must be configured: - `AWS_EMR_SHARED_FOLDER` +## AI Assistant MCP Tools + +The AI assistant supports [Model Context Protocol (MCP)][mcp-spec] servers, which extend the assistant's capabilities by giving it access to external tools (e.g. biomedical literature databases, clinical trials registries, genomic variant databases, and more). + +MCP servers are declared in `config/mcp_servers.json`. By default that file ships with **[BioMCP][biomcp]** already configured and enabled: + +```json +{ + "version": "1.0", + "servers": { + "biomcp": { + "enabled": true, + "description": "Biomedical research: PubMed papers, bioRxiv, ClinicalTrials.gov, NCI, variants, OncoKB", + "transport": "stdio", + "command": "biomcp", + "args": ["run", "--mode", "stdio"], + "env": {} + } + } +} +``` + +### Activating MCP support + +MCP tools are **disabled by default**. To enable them you need to have the AI assistant enabled first (i.e. `OPENAI_API_KEY` and the related parameters set) and then: + +1. Open your `docker-compose.yml` (copied from `docker-compose_dist.yml`). +2. In the `backend` service, uncomment the environment variable: + ```yaml + ASSISTANT_MCP_CONFIG_PATH: '/config/mcp_servers.json' + ``` +3. In the same service, uncomment the volume mount so the config file is available inside the container: + ```yaml + - ./config/mcp_servers.json:/config/mcp_servers.json:ro + ``` +4. Make sure any MCP server declared with `transport: stdio` has its `command` installed and available in `PATH` inside the container. For BioMCP that means `biomcp-python` must be installed in the container image. +5. Redo the deployment with Docker. + +### Adding custom MCP servers + +You can extend the assistant with any MCP-compatible server by adding entries to `config/mcp_servers.json`. The loader supports three transport types: + +| Transport | Required fields | Optional fields | +|-----------|----------------|-----------------| +| `stdio` | `command` | `args`, `env` | +| `http` | `command`, `url` | `args`, `env` | +| `sse` | `command`, `url` | `args`, `env` | + +Set `"enabled": false` on any entry to disable it without removing it from the file. + +Example — adding a custom HTTP MCP server alongside BioMCP: + +```json +{ + "version": "1.0", + "servers": { + "biomcp": { + "enabled": true, + "transport": "stdio", + "command": "biomcp", + "args": ["run", "--mode", "stdio"], + "env": {} + }, + "my-custom-mcp": { + "enabled": true, + "description": "Custom internal tool server", + "transport": "http", + "command": "my-mcp-server", + "url": "http://my-mcp-host:8080/mcp", + "env": { + "MY_API_KEY": "secret" + } + } + } +} +``` + +After modifying the file, restart the backend service for the changes to take effect. No rebuild is required since the file is mounted as a read-only volume. + + ## Execution of tasks with Celery Multiomix uses [Celery][celery] to distribute the computational load of its most expensive tasks (such as correlation analysis, Biomarkers Feature Selection, static validations, Machine Learning model training, etc.). This requires the user to have a messaging broker, such as RabbitMQ or Redis, installed and configured. In this project, Redis is used and a worker is deployed for each of the execution queues serving a different type of task. The Docker configuration is left ready to run in Docker Compose or Docker Swarm and K8S. @@ -286,6 +379,8 @@ To import a `media` folder backup inside a new environment you must (from the ro 2. Run the script `./tools/import_media.sh`. +[mcp-spec]: https://modelcontextprotocol.io/ +[biomcp]: https://github.com/genomics-geek/biomcp [docker-swarm]: https://docs.docker.com/engine/swarm/ [modulector]: https://github.com/omics-datascience/modulector [bioapi]: https://github.com/omics-datascience/BioAPI diff --git a/Dockerfile b/Dockerfile index 6b106de2..8d900f25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,47 +1,64 @@ FROM python:3.12-slim-bookworm # Docker Files Vars -ARG LISTEN_PORT 8000 -ARG LISTEN_IP "0.0.0.0" +ARG LISTEN_PORT=8000 +ARG LISTEN_IP="0.0.0.0" # Default values for deploying with multiomix image -ENV LISTEN_PORT $LISTEN_PORT -ENV LISTEN_IP $LISTEN_IP -ENV DJANGO_SETTINGS_MODULE "multiomics_intermediate.settings_prod" -ENV RESULT_DATAFRAME_LIMIT_ROWS 500 -ENV TABLE_PAGE_SIZE 10 +ENV LISTEN_PORT=$LISTEN_PORT +ENV LISTEN_IP=$LISTEN_IP +ENV DJANGO_SETTINGS_MODULE="multiomics_intermediate.settings_prod" +ENV RESULT_DATAFRAME_LIMIT_ROWS=500 +ENV TABLE_PAGE_SIZE=10 # Modulector connection parameters -ENV MODULECTOR_HOST "127.0.0.1" -ENV MODULECTOR_PORT "8001" +ENV MODULECTOR_HOST="127.0.0.1" +ENV MODULECTOR_PORT="8001" # BioAPI connection parameters -ENV BIOAPI_HOST "127.0.0.1" -ENV BIOAPI_PORT "8002" +ENV BIOAPI_HOST="127.0.0.1" +ENV BIOAPI_PORT="8002" # PostgreSQL DB connection parameters -ENV POSTGRES_USERNAME "multiomics" -ENV POSTGRES_PASSWORD "multiomics" -ENV POSTGRES_HOST "db" -ENV POSTGRES_PORT 5432 -ENV POSTGRES_DB "multiomics" +ENV POSTGRES_USERNAME="multiomics" +ENV POSTGRES_PASSWORD="multiomics" +ENV POSTGRES_HOST="db" +ENV POSTGRES_PORT=5432 +ENV POSTGRES_DB="multiomics" # Mongo DB connection parameters -ENV MONGO_USERNAME "multiomics" -ENV MONGO_PASSWORD "multiomics" -ENV MONGO_HOST "mongo" -ENV MONGO_PORT 27017 -ENV MONGO_DB "multiomics" +ENV MONGO_USERNAME="multiomics" +ENV MONGO_PASSWORD="multiomics" +ENV MONGO_HOST="mongo" +ENV MONGO_PORT=27017 +ENV MONGO_DB="multiomics" # Redis -ENV REDIS_HOST "redis" -ENV REDIS_PORT 6379 +ENV REDIS_HOST="redis" +ENV REDIS_PORT=6379 # Installs system dependencies and Node.js RUN apt-get update && apt-get install -y python3-pip curl libcurl4-openssl-dev libssl-dev libxml2-dev \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs && mkdir /config \ && mkdir /src +# Install R 4.4.2 from CRAN (Debian bookworm) and system dependencies +RUN set -eux; \ + echo "deb http://deb.debian.org/debian sid main" > /etc/apt/sources.list.d/debian-unstable.list; \ + printf 'APT::Default-Release "%s";\n' "bookworm" > /etc/apt/apt.conf.d/00default-release; \ + echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/90no-recommends; \ + printf 'Package: *\nPin: release a=unstable\nPin-Priority: 50\n' > /etc/apt/preferences.d/99pin-unstable; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg build-essential gfortran libblas-dev liblapack-dev git \ + libssl-dev libcurl4-openssl-dev libxml2-dev; \ + apt-get install -y \ + r-base=4.2.2.20221110-2 r-base-dev=4.2.2.20221110-2 r-recommended=4.2.2.20221110-2; \ + rm -rf /var/lib/apt/lists/* + +# Install Bioconductor limma (stats and base come with R) +RUN R -q -e 'options(repos=c(CRAN="https://cloud.r-project.org")); install.packages("BiocManager"); BiocManager::install(version="3.16", ask=FALSE); BiocManager::install("limma", ask=FALSE, update=FALSE)' + # Installs Python dependencies and compiles the frontend ADD config/requirements.txt /config/ WORKDIR /src @@ -58,4 +75,3 @@ HEALTHCHECK --interval=5m --timeout=30s CMD ["/bin/bash", "-c", "/src/tools/chec ENTRYPOINT ["/bin/bash", "-c", "/src/entrypoint.sh"] EXPOSE $LISTEN_PORT - diff --git a/Dockerfile-celery b/Dockerfile-celery index ea55bcba..89b892b9 100644 --- a/Dockerfile-celery +++ b/Dockerfile-celery @@ -1,47 +1,64 @@ FROM python:3.12-slim-bookworm # Docker Files Vars -ARG LISTEN_PORT 8000 -ARG LISTEN_IP "0.0.0.0" +ARG LISTEN_PORT=8000 +ARG LISTEN_IP="0.0.0.0" # Default values for deploying with multiomix image -ENV LISTEN_PORT $LISTEN_PORT -ENV LISTEN_IP $LISTEN_IP -ENV DJANGO_SETTINGS_MODULE "multiomics_intermediate.settings_prod" -ENV RESULT_DATAFRAME_LIMIT_ROWS 500 -ENV TABLE_PAGE_SIZE 10 +ENV LISTEN_PORT=$LISTEN_PORT +ENV LISTEN_IP=$LISTEN_IP +ENV DJANGO_SETTINGS_MODULE="multiomics_intermediate.settings_prod" +ENV RESULT_DATAFRAME_LIMIT_ROWS=500 +ENV TABLE_PAGE_SIZE=10 # Modulector connection parameters -ENV MODULECTOR_HOST "127.0.0.1" -ENV MODULECTOR_PORT "8001" +ENV MODULECTOR_HOST="127.0.0.1" +ENV MODULECTOR_PORT="8001" # BioAPI connection parameters -ENV BIOAPI_HOST "127.0.0.1" -ENV BIOAPI_PORT "8002" +ENV BIOAPI_HOST="127.0.0.1" +ENV BIOAPI_PORT="8002" # PostgreSQL DB connection parameters -ENV POSTGRES_USERNAME "multiomics" -ENV POSTGRES_PASSWORD "multiomics" -ENV POSTGRES_HOST "db" -ENV POSTGRES_PORT 5432 -ENV POSTGRES_DB "multiomics" +ENV POSTGRES_USERNAME="multiomics" +ENV POSTGRES_PASSWORD="multiomics" +ENV POSTGRES_HOST="db" +ENV POSTGRES_PORT=5432 +ENV POSTGRES_DB="multiomics" # Mongo DB connection parameters -ENV MONGO_USERNAME "multiomics" -ENV MONGO_PASSWORD "multiomics" -ENV MONGO_HOST "mongo" -ENV MONGO_PORT 27017 -ENV MONGO_DB "multiomics" +ENV MONGO_USERNAME="multiomics" +ENV MONGO_PASSWORD="multiomics" +ENV MONGO_HOST="mongo" +ENV MONGO_PORT=27017 +ENV MONGO_DB="multiomics" # Redis -ENV REDIS_HOST "redis" -ENV REDIS_PORT 6379 +ENV REDIS_HOST="redis" +ENV REDIS_PORT=6379 # Installs system dependencies RUN apt-get update && apt-get install -y python3-pip curl libcurl4-openssl-dev libssl-dev libxml2-dev \ && mkdir /config \ && mkdir /src +# Install R 4.4.2 from CRAN (Debian bookworm) and system dependencies +RUN set -eux; \ + echo "deb http://deb.debian.org/debian sid main" > /etc/apt/sources.list.d/debian-unstable.list; \ + printf 'APT::Default-Release "%s";\n' "bookworm" > /etc/apt/apt.conf.d/00default-release; \ + echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/90no-recommends; \ + printf 'Package: *\nPin: release a=unstable\nPin-Priority: 50\n' > /etc/apt/preferences.d/99pin-unstable; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + ca-certificates curl gnupg build-essential gfortran libblas-dev liblapack-dev git \ + libssl-dev libcurl4-openssl-dev libxml2-dev; \ + apt-get install -y \ + r-base=4.2.2.20221110-2 r-base-dev=4.2.2.20221110-2 r-recommended=4.2.2.20221110-2; \ + rm -rf /var/lib/apt/lists/* + +# Install Bioconductor limma (stats and base come with R) +RUN R -q -e 'options(repos=c(CRAN="https://cloud.r-project.org")); install.packages("BiocManager"); BiocManager::install(version="3.16", ask=FALSE); BiocManager::install("limma", ask=FALSE, update=FALSE)' + # Installs Python dependencies ADD config/requirements_celery.txt /config/requirements.txt RUN pip install --upgrade pip && pip3 install -r /config/requirements.txt diff --git a/README.md b/README.md index 874a5f58..4832100c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Multiomix logo +Multiomix logo # Multiomix @@ -15,6 +15,8 @@ This document is focused on the **development** of the system. If you are lookin - Node JS >= `20.x` (tested version: `20.x`) - [Modulector][modulector] `2.2.0` - [BioAPI][bioapi] `1.2.1` +- R `4.4.2` (required for `differential-expression`) +- **PostgreSQL with [pgvector](https://github.com/pgvector/pgvector)** (required for the AI assistant's semantic search). The provided Docker configuration already uses the `pgvector/pgvector:pg16` image — no manual installation needed when using Docker. ## Installation @@ -38,6 +40,7 @@ This document is focused on the **development** of the system. If you are lookin 1. `docker volume create --name=multiomics_intermediate_media_data` 1. `docker volume create --name=multiomics_intermediate_logs_data` 1. Test that all the services start correctly: `docker-compose -f docker-compose.dev.yml up -d` + > **Note:** the PostgreSQL service now uses the `pgvector/pgvector:pg16` image instead of the standard `postgres` image. This is required for the AI assistant's vector similarity search. If you have an existing container running the standard image, recreate it with the new one. 1. Go back to the `src` folder to create the DB and an admin user: 1. `python3 manage.py makemigrations` 1. `python3 manage.py migrate` @@ -66,11 +69,22 @@ Every time you want to work with Multiomix, you need to follow the below steps: 1. `python3 -m celery -A multiomics_intermediate worker -l info -Q stats` 1. `python3 -m celery -A multiomics_intermediate worker -l info -Q inference` 1. `python3 -m celery -A multiomics_intermediate worker -l info -Q sync_datasets` + 1. `python3 -m celery -A multiomics_intermediate worker -l info -Q differential_expression` 1. If you want to check Task in the GUI you can run [Flower](https://flower.readthedocs.io/en/latest/index.html) `python3 -m celery -A multiomics_intermediate flower` **NOTE:** maybe in Windows is needed to add `--pool=solo` to the previous commands. Example: `python3 -m celery -A multiomics_intermediate worker -l info -Q correlation_analysis --concurrency 1 --pool=solo` +### Dockerfile changes + +The production `Dockerfile` was updated in this version: + +- **ENV syntax**: all `ENV` declarations were migrated to the `ENV KEY=value` format (recommended since Docker 20.10). +- **R 4.4.2 + limma**: a new build stage installs R and the `limma` Bioconductor package required for differential expression experiments. + +If you are building a custom image, make sure your Docker version supports the updated syntax. + + ### Linter and Typescript All the scripts mentioned below must be run inside the `src/frontend/static/frontend` folder. diff --git a/config/mcp_servers.json b/config/mcp_servers.json new file mode 100644 index 00000000..e79f2593 --- /dev/null +++ b/config/mcp_servers.json @@ -0,0 +1,13 @@ +{ + "version": "1.0", + "servers": { + "biomcp": { + "enabled": true, + "description": "Biomedical research: PubMed papers, bioRxiv, ClinicalTrials.gov, NCI, variants, OncoKB", + "transport": "stdio", + "command": "biomcp", + "args": ["run", "--mode", "stdio"], + "env": {} + } + } +} diff --git a/config/requirements.txt b/config/requirements.txt index 253e7564..0cc208d0 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -17,14 +17,25 @@ lifelines==0.27.8 mypy-extensions==1.0.0 mypy==1.11.1 pandas==2.2.2 -psutil==6.0.0 +psutil==7.2.2 psycopg2-binary==2.9.9 pymongo==4.6.3 redis==5.0.3 -requests==2.31.0 +requests==2.34.2 scikit-learn==1.3.2 scikit-survival==0.22.2 scipy==1.13.0 statsmodels==0.14.2 xlrd==2.0.1 openpyxl==3.1.5 +rpy2==3.6.1 +urllib3==2.5.0 +langchain==1.3.2 +langchain-openai==1.2.2 +langchain-community==0.4.2 +langchain-huggingface==1.2.2 +langchain-mcp-adapters==0.2.2 +pgvector==0.4.2 +sentence-transformers==3.0.0 +openai==2.38.0 +biomcp-cli==0.8.22 \ No newline at end of file diff --git a/config/requirements_celery.txt b/config/requirements_celery.txt index 859c0823..dfdb3b54 100644 --- a/config/requirements_celery.txt +++ b/config/requirements_celery.txt @@ -25,3 +25,12 @@ scipy==1.13.0 statsmodels==0.14.2 xlrd==2.0.1 openpyxl==3.1.5 +rpy2==3.6.1 +urllib3==2.5.0 +langchain==0.3.0 +langchain-openai==0.2.0 +langchain-community==0.3.0 +langchain-huggingface==0.1.0 +pgvector==0.3.6 +sentence-transformers==3.0.0 +openai==1.50.0 \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 84459560..1e302ae7 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,7 +1,7 @@ services: # PostgreSQL db: - image: postgres:16 + image: pgvector/pgvector:pg16 restart: 'always' container_name: multiomics_dev_db ports: diff --git a/docker-compose_dist.yml b/docker-compose_dist.yml index ef3df0fe..d8a4c810 100644 --- a/docker-compose_dist.yml +++ b/docker-compose_dist.yml @@ -68,10 +68,31 @@ services: volumes: - media_data:/src/media environment: + # Common variables including the needed SECRET_KEY for production + <<: *common-variables + # Celery parameters QUEUE_NAME: 'correlation_analysis' # This MUST NOT be changed CONCURRENCY: 2 + # PostgreSQL DB connection parameters (MUST match the parameters defined in the "db" service above) + # POSTGRES_USERNAME: 'multiomics' + # POSTGRES_PASSWORD: 'multiomics' + # POSTGRES_HOST: 'db' + # POSTGRES_PORT: 5432 + # POSTGRES_DB: 'multiomics' + + # Mongo DB connection parameters (MUST match the parameters defined in the "mongo" service above) + # MONGO_USERNAME: 'multiomics' + # MONGO_PASSWORD: 'multiomics' + # MONGO_HOST: 'mongo' + # MONGO_PORT: 27017 + # MONGO_DB: 'multiomics' + + # Redis + # REDIS_HOST: 'redis' + # REDIS_PORT: 6379 + # Celery worker for feature selection fs-experiments-worker: image: omicsdatascience/multiomix:5.6.0-celery @@ -82,10 +103,31 @@ services: volumes: - media_data:/src/media environment: + # Common variables including the needed SECRET_KEY for production + <<: *common-variables + # Celery parameters QUEUE_NAME: 'feature_selection' # This MUST NOT be changed CONCURRENCY: 2 + # PostgreSQL DB connection parameters (MUST match the parameters defined in the "db" service above) + # POSTGRES_USERNAME: 'multiomics' + # POSTGRES_PASSWORD: 'multiomics' + # POSTGRES_HOST: 'db' + # POSTGRES_PORT: 5432 + # POSTGRES_DB: 'multiomics' + + # Mongo DB connection parameters (MUST match the parameters defined in the "mongo" service above) + # MONGO_USERNAME: 'multiomics' + # MONGO_PASSWORD: 'multiomics' + # MONGO_HOST: 'mongo' + # MONGO_PORT: 27017 + # MONGO_DB: 'multiomics' + + # Redis + # REDIS_HOST: 'redis' + # REDIS_PORT: 6379 + # Celery worker for statistical validation stats-worker: image: omicsdatascience/multiomix:5.6.0-celery @@ -96,10 +138,31 @@ services: volumes: - media_data:/src/media environment: + # Common variables including the needed SECRET_KEY for production + <<: *common-variables + # Celery parameters QUEUE_NAME: 'stats' # This MUST NOT be changed CONCURRENCY: 2 + # PostgreSQL DB connection parameters (MUST match the parameters defined in the "db" service above) + # POSTGRES_USERNAME: 'multiomics' + # POSTGRES_PASSWORD: 'multiomics' + # POSTGRES_HOST: 'db' + # POSTGRES_PORT: 5432 + # POSTGRES_DB: 'multiomics' + + # Mongo DB connection parameters (MUST match the parameters defined in the "mongo" service above) + # MONGO_USERNAME: 'multiomics' + # MONGO_PASSWORD: 'multiomics' + # MONGO_HOST: 'mongo' + # MONGO_PORT: 27017 + # MONGO_DB: 'multiomics' + + # Redis + # REDIS_HOST: 'redis' + # REDIS_PORT: 6379 + # Celery worker for inference inference-worker: image: omicsdatascience/multiomix:5.6.0-celery @@ -110,10 +173,31 @@ services: volumes: - media_data:/src/media environment: + # Common variables including the needed SECRET_KEY for production + <<: *common-variables + # Celery parameters QUEUE_NAME: 'inference' # This MUST NOT be changed CONCURRENCY: 2 + # PostgreSQL DB connection parameters (MUST match the parameters defined in the "db" service above) + # POSTGRES_USERNAME: 'multiomics' + # POSTGRES_PASSWORD: 'multiomics' + # POSTGRES_HOST: 'db' + # POSTGRES_PORT: 5432 + # POSTGRES_DB: 'multiomics' + + # Mongo DB connection parameters (MUST match the parameters defined in the "mongo" service above) + # MONGO_USERNAME: 'multiomics' + # MONGO_PASSWORD: 'multiomics' + # MONGO_HOST: 'mongo' + # MONGO_PORT: 27017 + # MONGO_DB: 'multiomics' + + # Redis + # REDIS_HOST: 'redis' + # REDIS_PORT: 6379 + # Celery worker for syncing datasets sync-datasets-worker: image: omicsdatascience/multiomix:5.6.0-celery @@ -124,10 +208,46 @@ services: volumes: - media_data:/src/media environment: + # Common variables including the needed SECRET_KEY for production + <<: *common-variables + # Celery parameters QUEUE_NAME: 'sync_datasets' # This MUST NOT be changed CONCURRENCY: 1 # NOTE: concurrency is set to minimum by default for this service + # PostgreSQL DB connection parameters (MUST match the parameters defined in the "db" service above) + # POSTGRES_USERNAME: 'multiomics' + # POSTGRES_PASSWORD: 'multiomics' + # POSTGRES_HOST: 'db' + # POSTGRES_PORT: 5432 + # POSTGRES_DB: 'multiomics' + + # Mongo DB connection parameters (MUST match the parameters defined in the "mongo" service above) + # MONGO_USERNAME: 'multiomics' + # MONGO_PASSWORD: 'multiomics' + # MONGO_HOST: 'mongo' + # MONGO_PORT: 27017 + # MONGO_DB: 'multiomics' + + # Redis + # REDIS_HOST: 'redis' + # REDIS_PORT: 6379 + + # Celery worker for differential expression + differential-expression-worker: + image: omicsdatascience/multiomix:5.6.0-celery + restart: 'always' + depends_on: + - db + - mongo + volumes: + - media_data:/src/media + environment: + <<: *common-variables + QUEUE_NAME: 'differential_expression' # This MUST NOT be changed + CONCURRENCY: 2 + # PostgreSQL, Mongo y Redis usan los valores por defecto del resto de servicios + # Django backend service multiomix: image: omicsdatascience/multiomix:5.6.0 @@ -173,10 +293,15 @@ services: # For logging (by default LOG_FILE_PATH is /logs) # LOG_FILE_PATH: /path/to/folder/log + + # MCP external tools (optional). Uncomment both lines below and install + # biomcp-python inside the container image to enable biomedical literature tools. + # ASSISTANT_MCP_CONFIG_PATH: '/config/mcp_servers.json' volumes: - static_data:/src/static - media_data:/src/media - logs_data:/logs + # - ./config/mcp_servers.json:/config/mcp_servers.json:ro depends_on: - db - mongo @@ -185,6 +310,7 @@ services: - stats-worker - inference-worker - sync-datasets-worker + - differential-expression-worker volumes: mongo_data: diff --git a/src/api_service/admin.py b/src/api_service/admin.py index a340f937..898eaea6 100644 --- a/src/api_service/admin.py +++ b/src/api_service/admin.py @@ -5,7 +5,7 @@ class ExperimentAdmin(admin.ModelAdmin): list_display = ('name', 'description', 'type', 'submit_date', 'state', 'user') - list_filter = ('state', 'submit_date') + list_filter = ('state', 'submit_date', 'tissue') search_fields = ('name', 'description', 'user__username') filter_horizontal = ('shared_institutions', 'shared_users') diff --git a/src/api_service/migrations/0062_alter_experiment_clinical_source_and_more.py b/src/api_service/migrations/0062_alter_experiment_clinical_source_and_more.py new file mode 100644 index 00000000..c86db344 --- /dev/null +++ b/src/api_service/migrations/0062_alter_experiment_clinical_source_and_more.py @@ -0,0 +1,142 @@ +# Generated by Django 4.2.19 on 2026-01-14 21:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "datasets_synchronization", + "0036_alter_cgdsstudy_clinical_patient_dataset_and_more", + ), + ("genes", "0002_auto_20210114_2331"), + ("user_files", "0015_alter_userfile_options"), + ("api_service", "0061_alter_experiment_shared_users"), + ] + + operations = [ + migrations.AlterField( + model_name="experiment", + name="clinical_source", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="experiments_as_clinical_source", + to="api_service.experimentclinicalsource", + ), + ), + migrations.AlterField( + model_name="experiment", + name="gem_source", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="experiments_as_gem_source", + to="api_service.experimentsource", + ), + ), + migrations.AlterField( + model_name="experiment", + name="mRNA_source", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="experiments_as_mrna_source", + to="api_service.experimentsource", + ), + ), + migrations.AlterField( + model_name="experimentclinicalsource", + name="extra_cgds_dataset", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="experiment_clinical_sources_as_extra_cgds_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="experimentsource", + name="cgds_dataset", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="experiment_sources_as_cgds_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="experimentsource", + name="user_file", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="experiment_sources_as_user_file", + to="user_files.userfile", + ), + ), + migrations.AlterField( + model_name="genecnacombination", + name="experiment", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_service.experiment", + ), + ), + migrations.AlterField( + model_name="genecnacombination", + name="gene", + field=models.ForeignKey( + db_column="gene", + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="%(class)ss_as_gene", + to="genes.gene", + ), + ), + migrations.AlterField( + model_name="genemethylationcombination", + name="experiment", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_service.experiment", + ), + ), + migrations.AlterField( + model_name="genemethylationcombination", + name="gene", + field=models.ForeignKey( + db_column="gene", + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="%(class)ss_as_gene", + to="genes.gene", + ), + ), + migrations.AlterField( + model_name="genemirnacombination", + name="experiment", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_service.experiment", + ), + ), + migrations.AlterField( + model_name="genemirnacombination", + name="gene", + field=models.ForeignKey( + db_column="gene", + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="%(class)ss_as_gene", + to="genes.gene", + ), + ), + ] diff --git a/src/api_service/migrations/0063_experiment_tissues.py b/src/api_service/migrations/0063_experiment_tissues.py new file mode 100644 index 00000000..e0aa4b97 --- /dev/null +++ b/src/api_service/migrations/0063_experiment_tissues.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.19 on 2026-06-19 22:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0002_tissue_code_and_initial_data"), + ("api_service", "0062_alter_experiment_clinical_source_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="experiment", + name="tissues", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tissues.tissue", + ), + ), + ] diff --git a/src/api_service/migrations/0064_rename_tissues_experiment_tissue.py b/src/api_service/migrations/0064_rename_tissues_experiment_tissue.py new file mode 100644 index 00000000..937e836d --- /dev/null +++ b/src/api_service/migrations/0064_rename_tissues_experiment_tissue.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2026-07-02 22:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("api_service", "0063_experiment_tissues"), + ] + + operations = [ + migrations.RenameField( + model_name="experiment", + old_name="tissues", + new_name="tissue", + ), + ] diff --git a/src/api_service/models.py b/src/api_service/models.py index 69648593..aa55fd61 100644 --- a/src/api_service/models.py +++ b/src/api_service/models.py @@ -1,24 +1,25 @@ import datetime from typing import Iterable, List, Optional, Type, Set, Any -from django.contrib.auth.base_user import AbstractBaseUser +import numpy as np +import pandas as pd +from django.contrib.auth import get_user_model from django.contrib.auth.models import User from django.db import models -from django.contrib.auth import get_user_model -from django.db.models import QuerySet, Q +from django.db.models import QuerySet + from common.constants import PATIENT_ID_COLUMN, SAMPLE_ID_COLUMN, SAMPLES_TYPE_COLUMN, PRIMARY_TYPE_VALUE from common.methylation import get_methylation_platform_dataframe +from datasets_synchronization.models import CGDSDataset from genes.models import Gene from inferences.models import InferenceExperiment from institutions.models import Institution from tags.models import Tag +from tissues.models import Tissue from user_files.models import UserFile from user_files.models_choices import FileType from .models_choices import ExperimentType, ExperimentState, CorrelationMethod, PValuesAdjustmentMethod from .websocket_functions import send_update_experiments_command -from datasets_synchronization.models import CGDSDataset -import pandas as pd -import numpy as np def get_combination_class(experiment_type: ExperimentType): @@ -45,10 +46,21 @@ class ExperimentSource(models.Model): inference_experiments_as_mrna: QuerySet[InferenceExperiment] gem_source: QuerySet['Experiment'] mrna_source: QuerySet['Experiment'] - user_file = models.ForeignKey(UserFile, on_delete=models.CASCADE, blank=True, null=True, - related_name='user_file') - cgds_dataset: CGDSDataset = models.ForeignKey(CGDSDataset, on_delete=models.CASCADE, blank=True, - null=True, related_name='cgds_dataset') + + user_file = models.ForeignKey( + UserFile, + on_delete=models.CASCADE, + blank=True, + null=True, + related_name='experiment_sources_as_user_file' + ) + cgds_dataset: CGDSDataset = models.ForeignKey( + CGDSDataset, + on_delete=models.CASCADE, + blank=True, + null=True, + related_name='experiment_sources_as_cgds_dataset' + ) def get_valid_source(self) -> UserFile | CGDSDataset: """ @@ -134,8 +146,13 @@ class ExperimentClinicalSource(ExperimentSource): """ inference_experiments: QuerySet[InferenceExperiment] clinical_source: QuerySet['Experiment'] - extra_cgds_dataset: CGDSDataset = models.ForeignKey('datasets_synchronization.CGDSDataset', - on_delete=models.CASCADE, blank=True, null=True) + extra_cgds_dataset: CGDSDataset = models.ForeignKey( + 'datasets_synchronization.CGDSDataset', + on_delete=models.CASCADE, + blank=True, + null=True, + related_name='experiment_clinical_sources_as_extra_cgds_dataset' + ) def get_methylation_platform_df(self): """ @@ -320,7 +337,9 @@ def number_of_rows(self) -> int: """ if self.user_file: return self.user_file.number_of_rows - return self.__get_cgds_datasets_joined_df().shape[0] + if self.cgds_dataset: + return self.__get_cgds_datasets_joined_df().shape[0] + return 0 @property def number_of_samples(self) -> int: @@ -330,7 +349,9 @@ def number_of_samples(self) -> int: """ if self.user_file: return self.user_file.number_of_samples - return self.__get_cgds_datasets_joined_df().shape[1] + if self.cgds_dataset: + return self.__get_cgds_datasets_joined_df().shape[1] + return 0 class Experiment(models.Model): @@ -338,14 +359,23 @@ class Experiment(models.Model): name: str = models.CharField(max_length=100) description: Optional[str] = models.TextField(blank=True, null=True) - mRNA_source = models.ForeignKey('ExperimentSource', on_delete=models.CASCADE, - related_name='mrna_source') - gem_source: ExperimentSource = models.ForeignKey('ExperimentSource', on_delete=models.CASCADE, - related_name='gem_source') - clinical_source: ExperimentClinicalSource = models.ForeignKey('api_service.ExperimentClinicalSource', - on_delete=models.SET_NULL, - related_name='clinical_source', blank=True, - null=True) + mRNA_source = models.ForeignKey( + 'ExperimentSource', + on_delete=models.CASCADE, + related_name='experiments_as_mrna_source' + ) + gem_source: ExperimentSource = models.ForeignKey( + 'ExperimentSource', + on_delete=models.CASCADE, + related_name='experiments_as_gem_source' + ) + clinical_source: ExperimentClinicalSource = models.ForeignKey( + 'api_service.ExperimentClinicalSource', + on_delete=models.SET_NULL, + related_name='experiments_as_clinical_source', + blank=True, + null=True + ) submit_date: datetime.datetime = models.DateTimeField(auto_now_add=True, blank=False, null=True) minimum_coefficient_threshold: float = models.FloatField(default=0.7) minimum_std_gene: float = models.FloatField(default=0.0) @@ -372,10 +402,9 @@ class Experiment(models.Model): # TODO: this can be stored in the Methylation type entity. Set the corresponding nullity in the new schema correlate_with_all_genes: bool = models.BooleanField(blank=False, null=False, default=True) - shared_institutions = models.ManyToManyField(Institution, blank=True, - related_name='shared_correlation_analysis') - shared_users = models.ManyToManyField(User, blank=True, - related_name='shared_users_correlation_analysis') + shared_institutions = models.ManyToManyField(Institution, blank=True, related_name='shared_correlation_analysis') + shared_users = models.ManyToManyField(User, blank=True, related_name='shared_users_correlation_analysis') + tissue = models.ForeignKey(Tissue, on_delete=models.SET_NULL, default=None, blank=True, null=True) is_public = models.BooleanField(blank=False, null=False, default=False) @property @@ -393,7 +422,7 @@ def get_combination_class(self): """ return get_combination_class(self.type) - def get_clinical_columns(self): + def get_clinical_columns(self) -> list[str]: """ Gets a list of columns from the clinical data @return: List of fields in clinical data @@ -423,12 +452,22 @@ class GeneGEMCombination(models.Model): id = models.BigAutoField(primary_key=True) # It's set as string foreign key to sort by Django Rest Framework # No DB constraint due to missing genes extra data - gene = models.ForeignKey(Gene, db_column='gene', on_delete=models.DO_NOTHING, db_constraint=False) + gene = models.ForeignKey( + Gene, + db_column='gene', + on_delete=models.DO_NOTHING, + db_constraint=False, + related_name='%(class)ss_as_gene' + ) gem = models.CharField(max_length=50) correlation = models.FloatField() p_value = models.FloatField() adjusted_p_value = models.FloatField(blank=True, null=True) - experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE) + experiment = models.ForeignKey( + Experiment, + on_delete=models.CASCADE, + related_name='%(class)ss' + ) source_statistical_data = models.OneToOneField( 'statistical_properties.SourceDataStatisticalProperties', diff --git a/src/api_service/serializers.py b/src/api_service/serializers.py index b3461d65..21eeef54 100644 --- a/src/api_service/serializers.py +++ b/src/api_service/serializers.py @@ -49,7 +49,7 @@ class ExperimentSerializerDetail(serializers.ModelSerializer): class Meta: model = Experiment - fields = ['id', 'name', 'description', 'tag'] + fields = ['id', 'name', 'description', 'tag', 'tissue'] class ExperimentSourceSerializer(serializers.ModelSerializer): @@ -72,12 +72,19 @@ class Meta: fields = ['id', 'username'] +class SimpleTissueSerializer(serializers.Serializer): + """Lightweight serializer for Tissue model""" + id = serializers.IntegerField() + name = serializers.CharField() + code = serializers.CharField() + class ExperimentSerializer(serializers.ModelSerializer): """Experiment serializer""" mRNA_source = ExperimentSourceSerializer() gem_source = ExperimentSourceSerializer() user = LimitedUserSerializer() tag = TagSerializer() + tissue = SimpleTissueSerializer(read_only=True) class Meta: model = Experiment @@ -99,7 +106,8 @@ class Meta: 'tag', 'clinical_source_id', 'is_public', - 'user' + 'user', + 'tissue' ] diff --git a/src/api_service/utils.py b/src/api_service/utils.py index 511cc9fa..ff242e33 100644 --- a/src/api_service/utils.py +++ b/src/api_service/utils.py @@ -131,5 +131,7 @@ def get_cgds_dataset(cgds_study: CGDSStudy, file_type: FileType) -> Optional[CGD return cgds_study.cna_dataset elif file_type == FileType.METHYLATION: return cgds_study.methylation_dataset + elif file_type == FileType.CLINICAL: + return cgds_study.clinical_patient_dataset else: return None diff --git a/src/api_service/views.py b/src/api_service/views.py index c0566d7f..901da96b 100644 --- a/src/api_service/views.py +++ b/src/api_service/views.py @@ -210,10 +210,10 @@ def get_queryset(self): experiment: Experiment = Experiment.objects.filter( Q(pk=experiment_id) & ( - Q(user=user) | - Q(is_public=True) | - Q(shared_institutions__institutionadministration__user=user) | - Q(shared_users=user) + Q(user=user) | + Q(is_public=True) | + Q(shared_institutions__institutionadministration__user=user) | + Q(shared_users=user) ) ).distinct().get() combinations_queryset = experiment.combinations @@ -293,6 +293,7 @@ def get_queryset(self): serializer_class = ExperimentSerializerDetail permission_classes = [permissions.IsAuthenticated, ExperimentIsNotRunning] + class RemoveInstitutionFromExperimentView(APIView): """ API endpoint to remove an institution from an experiment. @@ -300,7 +301,8 @@ class RemoveInstitutionFromExperimentView(APIView): """ permission_classes = [permissions.IsAuthenticated] - def post(self, request): + @staticmethod + def post(request): """ Remove an institution from the experiment. """ @@ -328,14 +330,16 @@ def post(self, request): {"message": f"Institution {institution.id} removed from experiment {experiment.id}."} ) + class RemoveUserFromExperimentView(APIView): """ - API endpoint to remove an user from an experiment. + API endpoint to remove a user from an experiment. Only the owner of the experiment can perform this action. """ permission_classes = [permissions.IsAuthenticated] - def post(self, request): + @staticmethod + def post(request): """ Remove an institution from the experiment. """ @@ -363,6 +367,7 @@ def post(self, request): {"message": f"User {user.id} removed from experiment {experiment.id}."} ) + class ToggleExperimentPublicView(APIView): """ API endpoint to toggle the 'is_public' field of an experiment. @@ -370,7 +375,8 @@ class ToggleExperimentPublicView(APIView): """ permission_classes = [permissions.IsAuthenticated] - def post(self, request): + @staticmethod + def post(request): """ Toggle the 'is_public' field of the experiment. """ @@ -390,6 +396,7 @@ def post(self, request): {"id": experiment.id, "is_public": experiment.is_public} ) + class InstitutionNonExperimentsSharedListView(generics.ListAPIView): """ REST endpoint: Get all institution NOT associated with a specific experiment. @@ -409,6 +416,7 @@ def get_queryset(self): id__in=experiment.shared_institutions.values_list('id', flat=True) ) + class UsersNonExperimentsSharedListView(generics.ListAPIView): """ REST endpoint: Get all users NOT associated with a specific experiment. @@ -426,6 +434,7 @@ def get_queryset(self): associated_user_ids = experiment.shared_users.values_list('id', flat=True) return get_user_model().objects.exclude(id__in=associated_user_ids) + class UsersExperimentsSharedListView(generics.ListAPIView): """ REST endpoint: Get all institution associated with a specific experiment. @@ -441,6 +450,7 @@ def get_queryset(self): experiment = get_object_or_404(Experiment, id=experiment_id) return experiment.shared_users + class InstitutionExperimentsSharedListView(generics.ListAPIView): """ REST endpoint: Get all institution associated with a specific experiment. @@ -456,13 +466,15 @@ def get_queryset(self): experiment = get_object_or_404(Experiment, id=experiment_id) return experiment.shared_institutions + class AddInstitutionToExperimentView(APIView): """ API endpoint to add an institution to an experiment. """ permission_classes = [permissions.IsAuthenticated] - def post(self, request): + @staticmethod + def post(request): data = request.data institution_id = data.get('institutionId') experiment_id = data.get('experimentId') @@ -472,7 +484,7 @@ def post(self, request): {"error": "Both 'institutionId' and 'experimentId' are required."}, status=status.HTTP_400_BAD_REQUEST ) - + experiment = get_object_or_404(Experiment, id=experiment_id) institution = get_object_or_404(Institution, id=institution_id) @@ -481,13 +493,15 @@ def post(self, request): serializer = InstitutionSerializer(institution) return Response(serializer.data) + class AddUserToExperimentView(APIView): """ API endpoint to add an institution to an experiment. """ permission_classes = [permissions.IsAuthenticated] - def post(self, request): + @staticmethod + def post(request): data = request.data user_id = data.get('userId') experiment_id = data.get('experimentId') @@ -497,7 +511,7 @@ def post(self, request): {"error": "Both 'institutionId' and 'experimentId' are required."}, status=status.HTTP_400_BAD_REQUEST ) - + experiment = get_object_or_404(Experiment, id=experiment_id) user = get_object_or_404(User, id=user_id) @@ -1201,14 +1215,14 @@ def post(request: Request): # Gets Gene and GEM expression with time values gene_values, gem_values, clinical_time_values, _gene_samples, _gem_samples, \ clinical_samples = pipelines.get_valid_data_from_sources( - experiment, - gene, - gem, - round_values=False, - return_samples_identifiers=True, - clinical_attribute=time_attribute, - fill_clinical_missing_samples=False - ) + experiment, + gene, + gem, + round_values=False, + return_samples_identifiers=True, + clinical_attribute=time_attribute, + fill_clinical_missing_samples=False + ) # Gets event values clinical_event_values: np.ndarray = experiment.clinical_source.get_specific_samples_and_attributes( diff --git a/src/api_service/websocket_functions.py b/src/api_service/websocket_functions.py index 237e34b9..9fc51255 100644 --- a/src/api_service/websocket_functions.py +++ b/src/api_service/websocket_functions.py @@ -40,7 +40,7 @@ def send_update_cgds_studies_command(): def send_update_biomarkers_command(user_id: int): """ - Sends a message indicating that an Biomarker's state update has occurred + Sends a message indicating that a Biomarker's state update has occurred """ user_group_name = f'notifications_{user_id}' message = { @@ -51,7 +51,7 @@ def send_update_biomarkers_command(user_id: int): def send_update_user_file_command(user_id: int): """ - Sends a message indicating that an user file's state update has occurred + Sends a message indicating that a user file's state update has occurred """ user_group_name = f'notifications_{user_id}' message = { @@ -84,6 +84,18 @@ def send_update_trained_models_command(user_id: int): send_message(user_group_name, message) +def send_update_differential_expression_experiments_command(user_id: int): + """ + Sends a message indicating that a DifferentialExpressionExperiment state update has occurred + @param user_id: DifferentialExpressionExperiment's user's id to send the WS message + """ + user_group_name = f'notifications_{user_id}' + message = { + 'command': 'update_differential_expression_experiments' + } + send_message(user_group_name, message) + + def send_update_prediction_experiment_command(user_id: int): """ Sends a message indicating that a InferenceExperiment state update has occurred @@ -107,9 +119,10 @@ def send_update_cluster_label_set_command(user_id: int): } send_message(user_group_name, message) + def send_update_institutions_command(user_id: int): """ - Sends a message indicating that a Institution state update has occurred + Sends a message indicating that an Institution state update has occurred @param user_id: Institution's user's id to send the WS message """ user_group_name = f'notifications_{user_id}' @@ -118,13 +131,14 @@ def send_update_institutions_command(user_id: int): } send_message(user_group_name, message) + def send_update_user_for_institution_command(user_id: int): """ - Sends a message indicating that a Institution_user state update has occurred + Sends a message indicating that an Institution_user state update has occurred @param user_id: Institution's user's id to send the WS message """ user_group_name = f'notifications_{user_id}' message = { 'command': 'update_user_for_institution' } - send_message(user_group_name, message) \ No newline at end of file + send_message(user_group_name, message) diff --git a/src/assistant/__init__.py b/src/assistant/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/assistant/admin.py b/src/assistant/admin.py new file mode 100644 index 00000000..51ecec54 --- /dev/null +++ b/src/assistant/admin.py @@ -0,0 +1,39 @@ +from django.contrib import admin +from .models import Conversation, Message, CuratedDocument + + +@admin.register(CuratedDocument) +class CuratedDocumentAdmin(admin.ModelAdmin): + list_display = ['title', 'category', 'is_active', 'updated_at'] + list_filter = ['is_active', 'category'] + search_fields = ['title', 'content'] + + def save_model(self, request, obj, form, change): + """Auto-generate embedding when saving a CuratedDocument.""" + super().save_model(request, obj, form, change) + try: + from assistant.services.embedding_service import embedding_service + text = f'{obj.title}\n{obj.content}' + obj.embedding = embedding_service.embed(text) + CuratedDocument.objects.filter(pk=obj.pk).update(embedding=obj.embedding) + except Exception as e: + self.message_user(request, f'Warning: could not generate embedding: {e}', level='warning') + + +@admin.register(Conversation) +class ConversationAdmin(admin.ModelAdmin): + list_display = ['id', 'user', 'title', 'created_at', 'updated_at'] + list_filter = ['user'] + search_fields = ['title', 'user__username'] + readonly_fields = ['created_at', 'updated_at'] + + +@admin.register(Message) +class MessageAdmin(admin.ModelAdmin): + list_display = ['id', 'conversation', 'role', 'content_preview', 'created_at'] + list_filter = ['role'] + readonly_fields = ['created_at', 'embedding'] + + def content_preview(self, obj): + return obj.content[:80] + content_preview.short_description = 'Content' diff --git a/src/assistant/apps.py b/src/assistant/apps.py new file mode 100644 index 00000000..843cb2ba --- /dev/null +++ b/src/assistant/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AssistantConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'assistant' diff --git a/src/assistant/migrations/0001_initial.py b/src/assistant/migrations/0001_initial.py new file mode 100644 index 00000000..d8ea5988 --- /dev/null +++ b/src/assistant/migrations/0001_initial.py @@ -0,0 +1,71 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import pgvector.django + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RunSQL( + sql='CREATE EXTENSION IF NOT EXISTS vector;', + reverse_sql='DROP EXTENSION IF EXISTS vector;', + ), + migrations.CreateModel( + name='Conversation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=200, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='conversations', + to=settings.AUTH_USER_MODEL, + )), + ], + options={ + 'ordering': ['-updated_at'], + }, + ), + migrations.CreateModel( + name='Message', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField( + choices=[('user', 'User'), ('assistant', 'Assistant')], + max_length=10, + )), + ('content', models.TextField()), + ('embedding', pgvector.django.VectorField(blank=True, dimensions=384, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('conversation', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='messages', + to='assistant.conversation', + )), + ], + options={ + 'ordering': ['created_at'], + }, + ), + migrations.CreateModel( + name='CuratedDocument', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=300)), + ('content', models.TextField()), + ('category', models.CharField(blank=True, max_length=100, null=True)), + ('embedding', pgvector.django.VectorField(blank=True, dimensions=384, null=True)), + ('is_active', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + ), + ] diff --git a/src/assistant/migrations/__init__.py b/src/assistant/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/assistant/models.py b/src/assistant/models.py new file mode 100644 index 00000000..abd60ae2 --- /dev/null +++ b/src/assistant/models.py @@ -0,0 +1,47 @@ +from django.contrib.auth.models import User +from django.db import models +from pgvector.django import VectorField + + +class Conversation(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='conversations') + title = models.CharField(max_length=200, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ['-updated_at'] + + def __str__(self): + return self.title or f'Conversation {self.pk}' + + +class Message(models.Model): + class Role(models.TextChoices): + USER = 'user', 'User' + ASSISTANT = 'assistant', 'Assistant' + + conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='messages') + role = models.CharField(max_length=10, choices=Role.choices) + content = models.TextField() + embedding = VectorField(dimensions=384, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ['created_at'] + + def __str__(self): + return f'{self.role}: {self.content[:50]}' + + +class CuratedDocument(models.Model): + title = models.CharField(max_length=300) + content = models.TextField() + category = models.CharField(max_length=100, null=True, blank=True) + embedding = VectorField(dimensions=384, null=True, blank=True) + is_active = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title diff --git a/src/assistant/serializers.py b/src/assistant/serializers.py new file mode 100644 index 00000000..a4406487 --- /dev/null +++ b/src/assistant/serializers.py @@ -0,0 +1,22 @@ +from rest_framework import serializers +from .models import Conversation, Message + + +class MessageSerializer(serializers.ModelSerializer): + class Meta: + model = Message + fields = ['id', 'role', 'content', 'created_at'] + + +class ConversationSerializer(serializers.ModelSerializer): + messages = MessageSerializer(many=True, read_only=True) + + class Meta: + model = Conversation + fields = ['id', 'title', 'created_at', 'updated_at', 'messages'] + + +class ConversationListSerializer(serializers.ModelSerializer): + class Meta: + model = Conversation + fields = ['id', 'title', 'created_at', 'updated_at'] diff --git a/src/assistant/services/__init__.py b/src/assistant/services/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/assistant/services/embedding_service.py b/src/assistant/services/embedding_service.py new file mode 100644 index 00000000..be1f6594 --- /dev/null +++ b/src/assistant/services/embedding_service.py @@ -0,0 +1,50 @@ +import os +from typing import List, Optional +from django.conf import settings + + +class EmbeddingService: + """Singleton lazy-loader for HuggingFace sentence-transformers embeddings.""" + + _instance: Optional['EmbeddingService'] = None + _model = None + + def __new__(cls) -> 'EmbeddingService': + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + + def _get_model(self): + if self._model is None: + from langchain_huggingface import HuggingFaceEmbeddings + + # Use HF_TOKEN if provided, otherwise run fully offline (model cached locally). + # Inference always runs locally — no text data is ever sent to HuggingFace. + hf_token = settings.ASSISTANT_HF_TOKEN + local_only = not hf_token and self._is_cached(settings.ASSISTANT_EMBEDDING_MODEL) + + self._model = HuggingFaceEmbeddings( + model_name=settings.ASSISTANT_EMBEDDING_MODEL, + model_kwargs={'device': 'cpu'}, + encode_kwargs={'normalize_embeddings': True}, + cache_folder=settings.ASSISTANT_HF_CACHE_DIR, + **({"huggingface_api_token": hf_token} if hf_token else {}), + ) + return self._model + + @staticmethod + def _is_cached(model_name: str) -> bool: + """Check if the model weights are already in the local HuggingFace cache.""" + cache_dir = settings.ASSISTANT_HF_HOME + model_slug = model_name.replace('/', '--') + hub_path = os.path.join(cache_dir, 'hub', f'models--{model_slug}') + return os.path.isdir(hub_path) + + def embed(self, text: str) -> List[float]: + return self._get_model().embed_query(text) + + def embed_many(self, texts: List[str]) -> List[List[float]]: + return self._get_model().embed_documents(texts) + + +embedding_service = EmbeddingService() diff --git a/src/assistant/services/llm_service.py b/src/assistant/services/llm_service.py new file mode 100644 index 00000000..15fcb6ba --- /dev/null +++ b/src/assistant/services/llm_service.py @@ -0,0 +1,191 @@ +import asyncio +import logging +from typing import List + +from django.conf import settings + +from assistant.models import Message +from assistant.services.embedding_service import embedding_service +from assistant.services.mcp_loader import load_mcp_config +from assistant.services.tools import make_tools +from langchain.agents import create_agent +from langchain_core.globals import set_debug +from langchain_core.messages import BaseMessage, HumanMessage, AIMessage +from langchain_openai import ChatOpenAI +from pgvector.django import CosineDistance + +logger = logging.getLogger(__name__) + +# Enable LangChain debug output only when the root logger is at DEBUG level +if logging.getLogger().isEnabledFor(logging.DEBUG): + set_debug(True) + +SYSTEM_PROMPT = """You are Multiomix Assistant, a specialized AI integrated into the Multiomix platform — +a cloud-based bioinformatics tool for inferring cancer genomic and epigenomic events associated with gene expression modulation. + +## Your scope + +You ONLY answer questions related to: +- The Multiomix platform (experiments, workflows, features, configuration) +- The user's own data: correlation experiments, biomarkers, feature selection runs, statistical validations, inference experiments +- Bioinformatics and computational biology concepts (gene expression, differential expression, miRNA, CNA, methylation, survival analysis, etc.) +- Cancer genomics and epigenomics +- Available cBioPortal (CGDS) datasets and studies +- Gene and molecule information (genes, miRNAs, CpG sites) +- Statistical methods used in the platform (DESeq2, limma, Cox regression, SVM, Random Forest, etc.) +- Protein-protein interaction networks and functional enrichment from STRING database +- Biomedical literature: PubMed papers, preprints (bioRxiv/medRxiv), clinical trials (ClinicalTrials.gov) +- Genomic variants and gene/drug/disease annotations from curated databases (when MCP tools are available) + +## Hard restrictions + +If the user asks about anything outside the scope above — including but not limited to: +general programming, current events, creative writing, cooking, travel, math unrelated to bioinformatics, +other software tools unrelated to bioinformatics, or any other off-topic subject — +respond ONLY with: +"I'm a specialized assistant for the Multiomix platform and bioinformatics. I can only help with questions +related to genomics, epigenomics, or your Multiomix experiments and data." + +Do NOT attempt to answer off-topic questions even partially. + +## Behavioral rules + +- Always be concise and scientifically accurate. +- When referencing user data (experiments, biomarkers, etc.), always retrieve it via the available tools — never guess or invent values. +- If you don't know something within your scope, say so clearly. +- Respond in the same language the user writes in. +- The chat interface renders markdown. You may use **bold**, *italic*, tables, bullet lists, + code blocks, and markdown image syntax `![alt](url)` freely in your responses. + When a STRING network URL is available, always embed it as `![STRING Network](url)`. +""" + + +def get_llm(): + """Returns the configured LLM. Swap this function to change provider.""" + return ChatOpenAI( + model=settings.ASSISTANT_LLM_MODEL, + temperature=settings.ASSISTANT_LLM_TEMPERATURE, + api_key=settings.OPENAI_API_KEY, + ) + + +def build_chat_history(conversation, user_id: int, query_embedding: List[float]) -> List[BaseMessage]: + """ + Build chat history: + - Recent messages: last N from the CURRENT conversation (immediate context). + - Semantic messages: most similar messages from ANY conversation of the user + (cross-chat long-term memory), excluding messages already in the recent set. + Returns LangChain message objects. + """ + + # Recent messages from the current conversation + recent_qs = Message.objects.filter(conversation=conversation).order_by('-created_at') + recent = list(recent_qs[:settings.ASSISTANT_RECENT_MESSAGES_COUNT]) + recent_ids = {m.pk for m in recent} + + # Semantic search across ALL user conversations (cross-chat memory) + semantic: List = [] + if query_embedding: + semantic_qs = ( + Message.objects + .filter(conversation__user_id=user_id, embedding__isnull=False) + .exclude(pk__in=recent_ids) + .annotate(distance=CosineDistance('embedding', query_embedding)) + .order_by('distance')[:settings.ASSISTANT_SEMANTIC_MESSAGES_COUNT] + ) + semantic = list(semantic_qs) + + all_messages = sorted(recent + semantic, key=lambda m: m.created_at) + + lc_messages: List[BaseMessage] = [] + for msg in all_messages: + if msg.role == 'user': + lc_messages.append(HumanMessage(content=msg.content)) + else: + lc_messages.append(AIMessage(content=msg.content)) + return lc_messages + + +def _extract_reply(result: dict) -> str: + """Extract text reply from the agent result dict.""" + messages = result.get('messages', []) + if not messages: + return '' + last = messages[-1] + content = last.content if hasattr(last, 'content') else '' + return content if isinstance(content, str) else str(content) + + +async def _async_agent(user_message: str, chat_history: List[BaseMessage], user_id: int, mcp_config: dict) -> str: + """ + Async agent execution. Loads MCP tools if mcp_config is provided, then invokes + the LangChain agent. Falls back gracefully to internal tools if MCP fails. + """ + from langchain_mcp_adapters.client import MultiServerMCPClient + + internal_tools = make_tools(user_id) + llm = get_llm() + + # In langchain 1.x the full message list (history + current turn) is the input. + messages: List[BaseMessage] = list(chat_history) + [HumanMessage(content=user_message)] + + if mcp_config: + try: + client = MultiServerMCPClient(mcp_config) + mcp_tools = await client.get_tools() + logger.debug('MCP tools loaded: %s', [t.name for t in mcp_tools]) + all_tools = internal_tools + mcp_tools + agent = create_agent(llm, all_tools, system_prompt=SYSTEM_PROMPT) + result = await agent.ainvoke({'messages': messages}) + return _extract_reply(result) + except Exception as exc: + logger.warning('MCP tools unavailable (%s), falling back to internal tools', exc) + + agent = create_agent(llm, internal_tools, system_prompt=SYSTEM_PROMPT) + result = await agent.ainvoke({'messages': messages}) + return _extract_reply(result) + + +def run_chat(conversation, user_message: str, user_id: int) -> str: + """ + Run one chat turn: embed the user message, build context, invoke agent, return reply. + Persists user + assistant messages (with embeddings) to the DB. + """ + + # Embed user query + query_embedding = embedding_service.embed(user_message) + + # Build chat history (messages BEFORE this turn, cross-chat semantic memory) + chat_history = build_chat_history(conversation, user_id, query_embedding) + + # Save user message to DB + Message.objects.create( + conversation=conversation, + role=Message.Role.USER, + content=user_message, + embedding=query_embedding, + ) + + # Update conversation title if this is the first message + if not conversation.title: + conversation.title = user_message[:100] + conversation.save(update_fields=['title']) + + mcp_config = load_mcp_config() + + # Async agent execution (MCP adapters require an event loop) + reply = asyncio.run(_async_agent(user_message, chat_history, user_id, mcp_config)) + + # Embed and save assistant reply + reply_embedding = embedding_service.embed(reply) + Message.objects.create( + conversation=conversation, + role=Message.Role.ASSISTANT, + content=reply, + embedding=reply_embedding, + ) + + # Touch conversation updated_at + conversation.save(update_fields=['updated_at']) + + return reply diff --git a/src/assistant/services/mcp_loader.py b/src/assistant/services/mcp_loader.py new file mode 100644 index 00000000..5f754f5f --- /dev/null +++ b/src/assistant/services/mcp_loader.py @@ -0,0 +1,65 @@ +import json +import logging +import os +import shutil + +from django.conf import settings + +logger = logging.getLogger(__name__) +VALID_TRANSPORTS = {'stdio', 'http', 'sse'} + + +def load_mcp_config() -> dict: + """ + Reads, validates and returns the MCP server config as a dict compatible with + MultiServerMCPClient. + Returns {} if config is absent, malformed, or has no enabled servers. + """ + config_path = getattr(settings, 'ASSISTANT_MCP_CONFIG_PATH', None) + if not config_path: + return {} + if not os.path.exists(config_path): + logger.warning('MCP config not found at: %s', config_path) + return {} + try: + with open(config_path) as f: + raw = json.load(f) + except (json.JSONDecodeError, IOError) as exc: + logger.error('Failed to read MCP config: %s', exc) + return {} + + servers = raw.get('servers', {}) + if not isinstance(servers, dict): + logger.error("MCP config 'servers' must be a dict") + return {} + + result = {} + for name, cfg in servers.items(): + if not cfg.get('enabled', True): + continue + transport = cfg.get('transport', '') + if transport not in VALID_TRANSPORTS: + logger.warning("MCP server '%s': invalid transport '%s', skipping", name, transport) + continue + if 'command' not in cfg: + logger.warning("MCP server '%s': missing 'command', skipping", name) + continue + if transport == 'stdio' and not shutil.which(cfg['command']): + logger.warning( + "MCP server '%s': command '%s' not found in PATH, skipping", + name, + cfg['command'], + ) + continue + + entry: dict = {'transport': transport, 'command': cfg['command']} + if 'args' in cfg: + entry['args'] = cfg['args'] + if cfg.get('env'): + entry['env'] = cfg['env'] + if transport in ('http', 'sse') and 'url' in cfg: + entry['url'] = cfg['url'] + result[name] = entry + logger.info("MCP server '%s' loaded (transport=%s)", name, transport) + + return result diff --git a/src/assistant/services/tools.py b/src/assistant/services/tools.py new file mode 100644 index 00000000..be3331d3 --- /dev/null +++ b/src/assistant/services/tools.py @@ -0,0 +1,645 @@ +import json +from typing import List +from langchain_core.tools import tool + + +def make_tools(user_id: int): + """ + Build LangChain tools with user_id baked in via closure. + The LLM never controls user_id — it is always injected server-side. + """ + + @tool + def get_user_experiments(limit: int = 20) -> str: + """ + Returns a list of the user's correlation analysis experiments (name, state, type, submit date). + Use this to answer questions about the user's pipeline experiments. + """ + from api_service.models import Experiment + qs = Experiment.objects.filter(user_id=user_id).order_by('-submit_date')[:limit] + data = list(qs.values('id', 'name', 'description', 'state', 'type', 'submit_date')) + return json.dumps(data, default=str) + + @tool + def get_user_biomarkers(limit: int = 20) -> str: + """ + Returns the user's biomarkers (own + public). Includes name, description, state. + Use this to answer questions about biomarkers. + """ + from django.db.models import Q + from biomarkers.models import Biomarker + qs = Biomarker.objects.filter( + Q(user_id=user_id) | Q(is_public=True) + ).distinct().order_by('-upload_date')[:limit] + data = list(qs.values('id', 'name', 'description', 'state', 'upload_date', 'is_public')) + return json.dumps(data, default=str) + + @tool + def get_statistical_validations(limit: int = 20) -> str: + """ + Returns statistical validations belonging to the user's biomarkers. + Use this to answer questions about statistical validation results (c_index, MSE, etc.). + """ + from statistical_properties.models import StatisticalValidation + qs = StatisticalValidation.objects.filter( + biomarker__user_id=user_id + ).order_by('-created')[:limit] + data = list(qs.values('id', 'name', 'description', 'state', 'c_index', 'mean_squared_error', 'created')) + return json.dumps(data, default=str) + + @tool + def get_inference_experiments(limit: int = 20) -> str: + """ + Returns inference experiments for the user's biomarkers. + Use this to answer questions about model inference results. + """ + from inferences.models import InferenceExperiment + qs = InferenceExperiment.objects.filter( + biomarker__user_id=user_id + ).order_by('-created')[:limit] + data = list(qs.values('id', 'name', 'description', 'state', 'created')) + return json.dumps(data, default=str) + + @tool + def get_feature_selection_experiments(limit: int = 20) -> str: + """ + Returns feature selection experiments owned by the user. + Use this to answer questions about feature selection runs and algorithms used. + """ + from feature_selection.models import FSExperiment + qs = FSExperiment.objects.filter(user_id=user_id).order_by('-id')[:limit] + data = list(qs.values('id', 'algorithm', 'execution_time')) + return json.dumps(data, default=str) + + @tool + def search_cgds_studies(query: str) -> str: + """ + Searches available cBioPortal (CGDS) studies by name. + Use this to find publicly available cancer genomics datasets. + """ + from datasets_synchronization.models import CGDSStudy + qs = CGDSStudy.objects.filter(name__icontains=query)[:10] + data = list(qs.values('id', 'name', 'description', 'url')) + return json.dumps(data, default=str) + + @tool + def get_gene_info(gene_name: str) -> str: + """ + Returns information about a gene (type, chromosome, start, end, description). + Use this to answer questions about specific genes. + """ + from genes.models import Gene + try: + gene = Gene.objects.get(name__iexact=gene_name) + return json.dumps({ + 'name': gene.name, + 'type': gene.type, + 'description': gene.description, + 'chromosome': gene.chromosome, + 'start': gene.start, + 'end': gene.end, + }) + except Gene.DoesNotExist: + return json.dumps({'error': f'Gene "{gene_name}" not found'}) + + @tool + def get_experiment_top_results(experiment_id: int, limit: int = 20) -> str: + """ + Returns the top correlation results (gene, GEM, correlation coefficient, p-value, + adjusted p-value) for a specific correlation experiment owned by the user. + Use this when the user asks about the results, top pairs, or significant correlations + of a specific experiment. Always verify the experiment belongs to the user. + Results are sorted by absolute correlation (strongest first). + """ + from api_service.models import Experiment + from api_service.models_choices import ExperimentState + + try: + exp = Experiment.objects.get(pk=experiment_id, user_id=user_id) + except Experiment.DoesNotExist: + return json.dumps({'error': f'Experiment {experiment_id} not found or does not belong to you'}) + + if exp.state != ExperimentState.COMPLETED: + return json.dumps({'error': f'Experiment is not completed (state={exp.state})'}) + + combination_class = exp.get_combination_class() + qs = ( + combination_class.objects + .filter(experiment=exp) + .order_by('-correlation') # strongest positive first + .values('gene_id', 'gem', 'correlation', 'p_value', 'adjusted_p_value')[:limit] + ) + results = list(qs) + return json.dumps({ + 'experiment_id': experiment_id, + 'experiment_name': exp.name, + 'type': exp.type, + 'total_results': exp.result_final_row_count, + 'top_results': results, + }, default=str) + + @tool + def get_differential_expression_results(experiment_id: int, limit: int = 20) -> str: + """ + Returns the top differentially expressed genes for a specific differential expression + experiment owned by the user. Results include gene name, log fold change (logFC), + adjusted p-value (FDR), p-value, average expression and t-statistic. + Results are sorted by adjusted p-value (most significant first). + Use this when the user asks about DE results, upregulated/downregulated genes, + or the output of a differential expression analysis. + """ + from differential_expression.models import DifferentialExpressionExperiment, DifferentialExpressionExperimentState + + try: + exp = DifferentialExpressionExperiment.objects.get(pk=experiment_id, user_id=user_id) + except DifferentialExpressionExperiment.DoesNotExist: + return json.dumps({'error': f'Differential expression experiment {experiment_id} not found or does not belong to you'}) + + if exp.state != DifferentialExpressionExperimentState.COMPLETED: + return json.dumps({'error': f'Experiment is not completed (state={exp.state})'}) + + results = list( + exp.results + .order_by('adj_p_val') + .values('gene', 'log_fc', 'adj_p_val', 'p_value', 'ave_expr', 't_statistic')[:limit] + ) + return json.dumps({ + 'experiment_id': experiment_id, + 'experiment_name': exp.name, + 'tool_used': exp.tool, + 'top_results': results, + }, default=str) + + @tool + def get_user_files(limit: int = 30) -> str: + """ + Returns the list of files uploaded by the user (name, type, number of samples, upload date). + Use this when the user asks about their datasets, uploaded files, or data sources. + File types: 1=mRNA, 2=miRNA, 3=CNA, 4=Methylation, 5=Clinical. + """ + from user_files.models import UserFile + qs = UserFile.objects.filter(user_id=user_id).order_by('-upload_date')[:limit] + data = list(qs.values('id', 'name', 'file_type', 'number_of_samples', 'upload_date')) + return json.dumps(data, default=str) + + @tool + def get_differential_expression_experiments(limit: int = 20) -> str: + """ + Returns the user's differential expression experiments (name, state, tool used, + number of results, creation date). + Use this to list or find differential expression analyses before fetching their results. + """ + from differential_expression.models import DifferentialExpressionExperiment + qs = DifferentialExpressionExperiment.objects.filter( + user_id=user_id + ).order_by('-created_at')[:limit] + data = list(qs.values('id', 'name', 'description', 'state', 'tool', 'created_at')) + return json.dumps(data, default=str) + + @tool + def get_string_interaction_partners(gene_name: str, limit: int = 10) -> str: + """ + Returns the top protein-protein interaction partners for a gene from the STRING database. + Results include partner gene name and interaction scores (combined, experimental, + textmining, databases, coexpression). + Use this when the user asks about protein interactions, interaction networks, + or which proteins interact with a specific gene. + Only human proteins (species 9606) are queried. + """ + import urllib.request + import urllib.parse + + params = urllib.parse.urlencode({ + 'identifiers': gene_name, + 'species': 9606, + 'limit': min(limit, 20), + 'caller_identity': 'multiomix_platform', + }) + url = f'https://string-db.org/api/json/interaction_partners?{params}' + try: + with urllib.request.urlopen(url, timeout=12) as resp: + data = json.loads(resp.read().decode()) + except Exception as e: + return json.dumps({'error': f'STRING API error: {str(e)}'}) + + if not data: + return json.dumps({'gene': gene_name, 'partners': [], 'message': 'No interactions found.'}) + + partners = [{ + 'partner': item.get('preferredName_B', ''), + 'combined_score': round(item.get('score', 0), 3), + 'experimental': round(item.get('escore', 0), 3), + 'textmining': round(item.get('tscore', 0), 3), + 'databases': round(item.get('dscore', 0), 3), + 'coexpression': round(item.get('ascore', 0), 3), + } for item in data] + + return json.dumps({'gene': gene_name, 'partners_shown': len(partners), 'partners': partners}) + + @tool + def get_string_functional_enrichment(gene_names: str) -> str: + """ + Returns functional enrichment analysis (GO Biological Process, GO Molecular Function, + GO Cellular Component, KEGG, Reactome, etc.) for a list of genes using the STRING database. + Provide gene_names as a comma-separated string (e.g., "TP53,BRCA1,MYC"). + Returns the top enriched terms sorted by false discovery rate (FDR). + Use this when the user asks about pathways, biological processes, molecular functions, + or wants to functionally annotate a gene set from their experiments. + """ + import urllib.request + import urllib.parse + + genes = [g.strip() for g in gene_names.split(',') if g.strip()] + if not genes: + return json.dumps({'error': 'No gene names provided.'}) + + # STRING accepts multiple identifiers separated by carriage return (\r → %0D) + params = urllib.parse.urlencode({ + 'identifiers': '\r'.join(genes), + 'species': 9606, + 'caller_identity': 'multiomix_platform', + }) + url = f'https://string-db.org/api/json/enrichment?{params}' + try: + with urllib.request.urlopen(url, timeout=15) as resp: + data = json.loads(resp.read().decode()) + except Exception as e: + return json.dumps({'error': f'STRING API error: {str(e)}'}) + + if not data: + return json.dumps({'genes': genes, 'enrichment': [], 'message': 'No enrichment results found.'}) + + top = sorted(data, key=lambda x: float(x.get('fdr', 1)))[:15] + enrichment = [{ + 'category': item.get('category', ''), + 'term': item.get('term', ''), + 'description': item.get('description', ''), + 'fdr': item.get('fdr'), + 'p_value': item.get('p_value'), + 'gene_count': item.get('number_of_genes'), + } for item in top] + + return json.dumps({'genes': genes, 'total_enriched_terms': len(data), 'top_terms': enrichment}) + + @tool + def get_string_network_url(gene_names: str) -> str: + """ + Returns the URL of a STRING protein interaction network image for a list of genes. + Provide gene_names as a comma-separated string (e.g., "TP53,BRCA1,MYC"). + Maximum 10 genes recommended for a readable network image. + IMPORTANT: after calling this tool, ALWAYS include the image in your reply using + markdown image syntax: ![STRING Network](url) — this will display the network + directly in the chat so the user can see it. + Use this when the user asks to visualize a protein network or wants a graphical + view of gene/protein interactions. + """ + import urllib.parse + + genes = [g.strip() for g in gene_names.split(',') if g.strip()][:10] + if not genes: + return json.dumps({'error': 'No gene names provided.'}) + + params = urllib.parse.urlencode({ + 'identifiers': '\r'.join(genes), + 'species': 9606, + 'caller_identity': 'multiomix_platform', + 'network_flavor': 'confidence', + }) + url = f'https://string-db.org/api/image/network?{params}' + + return json.dumps({ + 'genes': genes, + 'network_image_url': url, + 'markdown': f'![STRING Protein Network]({url})', + }) + + @tool + def get_survival_experiments(limit: int = 20) -> str: + """ + Returns the user's survival / statistical validation experiments with their metrics. + Each experiment belongs to one of the user's biomarkers. + Metrics: c_index, cox_c_index (Cox regression), cox_log_likelihood, r2_score, mean_squared_error. + Use this to list or find survival analysis runs before fetching detailed results. + """ + from statistical_properties.models import StatisticalValidation + qs = StatisticalValidation.objects.filter( + biomarker__user_id=user_id + ).order_by('-created')[:limit] + data = list(qs.values( + 'id', 'name', 'description', 'state', 'created', + 'c_index', 'cox_c_index', 'cox_log_likelihood', 'r2_score', 'mean_squared_error', + 'biomarker__id', 'biomarker__name', + )) + return json.dumps(data, default=str) + + @tool + def get_survival_results(experiment_id: int) -> str: + """ + Returns detailed survival analysis results for a specific statistical validation experiment. + Includes survival metrics (c_index, cox_c_index, cox_log_likelihood, r2_score, mean_squared_error) + and the list of molecules with their Cox regression coefficients. + A positive coefficient means the molecule increases risk; negative means protective. + Use this when the user asks about survival metrics or molecule coefficients of a specific + statistical validation. Always verify the experiment belongs to the user via their biomarkers. + """ + from statistical_properties.models import StatisticalValidation + + try: + sv = StatisticalValidation.objects.select_related('biomarker').get( + pk=experiment_id, biomarker__user_id=user_id + ) + except StatisticalValidation.DoesNotExist: + return json.dumps({'error': f'Survival validation {experiment_id} not found or does not belong to you'}) + + molecules = list(sv.molecules_with_coefficients.values('identifier', 'coeff', 'type')) + + return json.dumps({ + 'id': sv.id, + 'name': sv.name, + 'description': sv.description, + 'state': sv.state, + 'biomarker_id': sv.biomarker.id, + 'biomarker_name': sv.biomarker.name, + 'metrics': { + 'c_index': sv.c_index, + 'cox_c_index': sv.cox_c_index, + 'cox_log_likelihood': sv.cox_log_likelihood, + 'r2_score': sv.r2_score, + 'mean_squared_error': sv.mean_squared_error, + }, + 'molecules_with_coefficients': molecules, + }, default=str) + + @tool + def find_gene_across_experiments(gene_name: str) -> str: + """ + Searches across all the user's correlation experiments (miRNA, CNA, Methylation) to find + which ones contain a specific gene in their results. + Returns the experiment name, type, paired GEM molecule, and correlation statistics. + Use this when the user asks "in which experiments does gene X appear?", + wants to explore all correlations involving a specific gene, or wants to discover + which GEM molecules co-correlate with a gene across multiple experiments. + """ + from api_service.models import GeneMiRNACombination, GeneCNACombination, GeneMethylationCombination + + results = [] + + for CombClass, exp_type_label in [ + (GeneMiRNACombination, 'miRNA'), + (GeneCNACombination, 'CNA'), + (GeneMethylationCombination, 'Methylation'), + ]: + qs = ( + CombClass.objects + .filter(experiment__user_id=user_id, gene__name__iexact=gene_name) + .values( + 'experiment__id', 'experiment__name', 'experiment__state', + 'gem', 'correlation', 'p_value', 'adjusted_p_value', + ) + .order_by('-correlation')[:10] + ) + for row in qs: + results.append({ + 'experiment_id': row['experiment__id'], + 'experiment_name': row['experiment__name'], + 'experiment_type': exp_type_label, + 'gem': row['gem'], + 'correlation': row['correlation'], + 'p_value': row['p_value'], + 'adjusted_p_value': row['adjusted_p_value'], + }) + + if not results: + return json.dumps({ + 'gene': gene_name, + 'found_in': 0, + 'results': [], + 'message': f'Gene "{gene_name}" not found in any of your experiments.', + }) + + return json.dumps({'gene': gene_name, 'found_in': len(results), 'results': results}, default=str) + + @tool + def get_genes_in_biomarker(biomarker_id: int) -> str: + """ + Returns all molecules contained in a specific biomarker, grouped by type: + mRNAs (genes), miRNAs, CNAs, and methylation identifiers. + Use this when the user asks which genes or molecules are part of a biomarker, + or wants to review the composition of a biomarker before running further analyses. + """ + from django.db.models import Q + from biomarkers.models import Biomarker + + try: + biomarker = Biomarker.objects.get( + Q(user_id=user_id) | Q(is_public=True), + pk=biomarker_id, + ) + except Biomarker.DoesNotExist: + return json.dumps({'error': f'Biomarker {biomarker_id} not found or not accessible'}) + + return json.dumps({ + 'id': biomarker.id, + 'name': biomarker.name, + 'description': biomarker.description, + 'mrnas': list(biomarker.mrnas.values_list('identifier', flat=True)), + 'mirnas': list(biomarker.mirnas.values_list('identifier', flat=True)), + 'cnas': list(biomarker.cnas.values_list('identifier', flat=True)), + 'methylations': list(biomarker.methylations.values_list('identifier', flat=True)), + }) + + @tool + def get_experiment_detail(experiment_id: int) -> str: + """ + Returns the full configuration of a correlation experiment: which datasets were used + (mRNA source and GEM source — whether a user file or a cBioPortal dataset), the + correlation and p-value adjustment methods, filtering thresholds, and execution statistics. + Use this when the user asks what data was used in an experiment, what parameters were + applied, or wants a complete summary of an experiment's setup. + """ + from api_service.models import Experiment + + try: + exp = Experiment.objects.select_related( + 'mRNA_source__user_file', + 'mRNA_source__cgds_dataset', + 'gem_source__user_file', + 'gem_source__cgds_dataset', + ).get(pk=experiment_id, user_id=user_id) + except Experiment.DoesNotExist: + return json.dumps({'error': f'Experiment {experiment_id} not found or does not belong to you'}) + + def source_info(source): + if source is None: + return None + if source.user_file_id: + uf = source.user_file + return {'source_type': 'user_file', 'name': uf.name, 'file_type': uf.file_type} + if source.cgds_dataset_id: + ds = source.cgds_dataset + return { + 'source_type': 'cgds_dataset', + 'file_path': ds.file_path, + 'observation': ds.observation, + 'number_of_samples': ds.number_of_samples, + } + return None + + return json.dumps({ + 'id': exp.id, + 'name': exp.name, + 'description': exp.description, + 'type': exp.get_type_display(), + 'state': exp.get_state_display(), + 'submit_date': str(exp.submit_date), + 'mrna_source': source_info(exp.mRNA_source), + 'gem_source': source_info(exp.gem_source), + 'parameters': { + 'correlation_method': exp.get_correlation_method_display(), + 'p_values_adjustment_method': exp.get_p_values_adjustment_method_display(), + 'minimum_coefficient_threshold': exp.minimum_coefficient_threshold, + 'minimum_std_gene': exp.minimum_std_gene, + 'minimum_std_gem': exp.minimum_std_gem, + 'correlate_with_all_genes': exp.correlate_with_all_genes, + }, + 'results': { + 'evaluated_rows': exp.evaluated_row_count, + 'total_results': exp.result_total_row_count, + 'final_results': exp.result_final_row_count, + 'execution_time_seconds': exp.execution_time, + }, + }, default=str) + + @tool + def search_curated_knowledge(query: str) -> str: + """ + Searches the curated knowledge base using semantic similarity. + Use this to answer general questions about how Multiomix works, + biological concepts, or platform documentation. + """ + from pgvector.django import CosineDistance + from assistant.models import CuratedDocument + from assistant.services.embedding_service import embedding_service + + query_embedding = embedding_service.embed(query) + docs = ( + CuratedDocument.objects + .filter(is_active=True, embedding__isnull=False) + .annotate(distance=CosineDistance('embedding', query_embedding)) + .order_by('distance')[:5] + ) + data = [{'title': d.title, 'category': d.category, 'content': d.content} for d in docs] + return json.dumps(data) + + @tool + def get_mirna_modulators(gene_name: str, min_score: float = 0.0, limit: int = 20) -> str: + """ + Returns miRNAs known to target/regulate the expression of a specific gene, + sourced from Modulector's miRNA-target interaction database. + Use this when the user asks about miRNA modulators, expression regulators, + or GEM (Gene Expression Modulator) interactions for a gene. + Results include miRNA name, interaction score, and supporting evidence. + """ + from api_service.mrna_service import global_mrna_service + + params: dict = {'gene': gene_name} + if min_score > 0: + params['score'] = str(min_score) + + data = global_mrna_service.get_mdulector_service_content( + 'mirna-target-interactions', + request_params=params, + is_paginated=True, + method='get' + ) + + if not data or data.get('count', 0) == 0: + return json.dumps({ + 'message': f'No miRNA modulators found for gene "{gene_name}" in the database.', + 'results': [] + }) + + results = data.get('results', [])[:limit] + return json.dumps({ + 'gene': gene_name, + 'total_interactions': data.get('count', 0), + 'shown': len(results), + 'modulators': results + }, default=str) + + @tool + def get_gene_annotations(gene_name: str) -> str: + """ + Returns detailed biological annotations for a gene from BioAPI, + including aliases, biotype, summary, chromosomal location, and external database IDs + (Ensembl, NCBI, HGNC, etc.). + Use this when the user asks for biological context, description, or detailed + information about a specific gene beyond basic coordinates. + """ + from api_service.mrna_service import global_mrna_service + + data = global_mrna_service.get_bioapi_service_content( + 'information-of-genes', + request_params={'gene_ids': [gene_name]}, + is_paginated=False, + method='post' + ) + + if not data: + return json.dumps({'error': f'No annotation data found for gene "{gene_name}" in BioAPI.'}) + + return json.dumps({'gene': gene_name, 'annotations': data}, default=str) + + @tool + def get_drugs_regulating_gene(gene_name: str) -> str: + """ + Returns a DrugBank link listing drugs that up-regulate or down-regulate + the expression of a specific gene, sourced from BioAPI. + Use this when the user asks about pharmacological regulators, + drug modulators, or therapeutic targeting of a gene's expression. + """ + from api_service.mrna_service import global_mrna_service + + data = global_mrna_service.get_bioapi_service_content( + f'/drugs-regulating-gene/{gene_name}', + request_params={}, + is_paginated=False, + method='get' + ) + + if not data or 'link' not in data: + return json.dumps({ + 'message': f'No drug regulation data found for gene "{gene_name}" in BioAPI.', + 'link': None + }) + + return json.dumps({ + 'gene': gene_name, + 'drugbank_link': data.get('link') + }) + + return [ + get_user_experiments, + get_experiment_top_results, + get_experiment_detail, + get_user_biomarkers, + get_statistical_validations, + get_survival_experiments, + get_survival_results, + get_inference_experiments, + get_feature_selection_experiments, + search_cgds_studies, + get_gene_info, + get_gene_annotations, + get_mirna_modulators, + get_drugs_regulating_gene, + get_string_interaction_partners, + get_string_functional_enrichment, + get_string_network_url, + search_curated_knowledge, + get_user_files, + get_differential_expression_experiments, + get_differential_expression_results, + find_gene_across_experiments, + get_genes_in_biomarker, + ] diff --git a/src/assistant/urls.py b/src/assistant/urls.py new file mode 100644 index 00000000..e2c5b62c --- /dev/null +++ b/src/assistant/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from .views import ChatView, ConversationListView, ConversationDetailView + +urlpatterns = [ + path('api/chat/', ChatView.as_view(), name='assistant_chat'), + path('api/conversations/', ConversationListView.as_view(), name='assistant_conversations'), + path('api/conversations//', ConversationDetailView.as_view(), name='assistant_conversation_detail'), +] diff --git a/src/assistant/views.py b/src/assistant/views.py new file mode 100644 index 00000000..a2571c61 --- /dev/null +++ b/src/assistant/views.py @@ -0,0 +1,63 @@ +from django.shortcuts import get_object_or_404 +from rest_framework import generics, permissions, status +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + +from .models import Conversation +from .serializers import ConversationListSerializer, ConversationSerializer + + +class ChatView(APIView): + """POST /assistant/api/chat/ — send a message, get a reply.""" + + permission_classes = [permissions.IsAuthenticated] + + def post(self, request: Request) -> Response: + from .services.llm_service import run_chat + + message = request.data.get('message', '').strip() + conversation_id = request.data.get('conversation_id') + + if not message: + return Response({'error': 'message is required'}, status=status.HTTP_400_BAD_REQUEST) + + if conversation_id: + conversation = get_object_or_404(Conversation, pk=conversation_id, user=request.user) + else: + conversation = Conversation.objects.create(user=request.user) + + reply = run_chat(conversation, message, request.user.pk) + + return Response({ + 'conversation_id': conversation.pk, + 'reply': reply, + }) + + +class ConversationListView(generics.ListAPIView): + """GET /assistant/api/conversations/ — list user's conversations.""" + + permission_classes = [permissions.IsAuthenticated] + serializer_class = ConversationListSerializer + + def get_queryset(self): + return Conversation.objects.filter(user=self.request.user) + + +class ConversationDetailView(generics.RetrieveDestroyAPIView): + """GET/DELETE/PATCH /assistant/api/conversations// — get messages, delete, or rename.""" + + permission_classes = [permissions.IsAuthenticated] + serializer_class = ConversationSerializer + + def get_queryset(self): + return Conversation.objects.filter(user=self.request.user) + + def patch(self, request, *args, **kwargs): + """PATCH — update the conversation title only.""" + conversation = self.get_object() + title = (request.data.get('title') or '').strip() or None + conversation.title = title + conversation.save(update_fields=['title']) + return Response({'id': conversation.pk, 'title': conversation.title}) diff --git a/src/biomarkers/admin.py b/src/biomarkers/admin.py index a8835594..c52618fa 100644 --- a/src/biomarkers/admin.py +++ b/src/biomarkers/admin.py @@ -3,7 +3,7 @@ class BiomarkerAdmin(admin.ModelAdmin): list_display = ('name', 'description', 'tag', 'upload_date') - list_filter = ('tag', 'upload_date') + list_filter = ('tag', 'upload_date', 'tissue') search_fields = ('name', 'description') admin.site.register(Biomarker, BiomarkerAdmin) diff --git a/src/biomarkers/migrations/0023_biomarker_tissues.py b/src/biomarkers/migrations/0023_biomarker_tissues.py new file mode 100644 index 00000000..9028cc4a --- /dev/null +++ b/src/biomarkers/migrations/0023_biomarker_tissues.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.19 on 2026-06-23 03:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0002_tissue_code_and_initial_data"), + ("biomarkers", "0022_alter_biomarker_shared_users"), + ] + + operations = [ + migrations.AddField( + model_name="biomarker", + name="tissues", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tissues.tissue", + ), + ), + ] diff --git a/src/biomarkers/migrations/0024_rename_tissues_biomarker_tissue.py b/src/biomarkers/migrations/0024_rename_tissues_biomarker_tissue.py new file mode 100644 index 00000000..95108843 --- /dev/null +++ b/src/biomarkers/migrations/0024_rename_tissues_biomarker_tissue.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2026-07-02 22:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("biomarkers", "0023_biomarker_tissues"), + ] + + operations = [ + migrations.RenameField( + model_name="biomarker", + old_name="tissues", + new_name="tissue", + ), + ] diff --git a/src/biomarkers/models.py b/src/biomarkers/models.py index c510b5c6..2fc19491 100644 --- a/src/biomarkers/models.py +++ b/src/biomarkers/models.py @@ -8,6 +8,7 @@ from institutions.models import Institution from tags.models import Tag +from tissues.models import Tissue from api_service.websocket_functions import send_update_biomarkers_command from user_files.models_choices import MoleculeType @@ -76,6 +77,7 @@ class Biomarker(models.Model): shared_institutions = models.ManyToManyField(Institution, related_name='shared_biomarkers', blank=True) shared_users = models.ManyToManyField(User, blank=True, related_name='shared_users_biomarkers') + tissue = models.ForeignKey(Tissue, on_delete=models.SET_NULL, default=None, blank=True, null=True) def __str__(self) -> str: return self.name diff --git a/src/biomarkers/serializers.py b/src/biomarkers/serializers.py index 989a599f..06be1135 100644 --- a/src/biomarkers/serializers.py +++ b/src/biomarkers/serializers.py @@ -60,6 +60,13 @@ class Meta: exclude = ['biomarker'] +class SimpleTissueSerializer(serializers.Serializer): + """Lightweight serializer for Tissue model""" + id = serializers.IntegerField() + name = serializers.CharField() + code = serializers.CharField() + + class BiomarkerSimpleSerializer(WritableNestedModelSerializer): """Biomarker model serializer without the molecules (useful to list Biomarkers).""" number_of_mrnas = serializers.SerializerMethodField(method_name='get_number_of_mrnas') @@ -77,6 +84,11 @@ class Meta: model = Biomarker fields = '__all__' + def to_representation(self, instance): + data = super().to_representation(instance) + data['tissue'] = SimpleTissueSerializer(instance.tissue).data if instance.tissue else None + return data + @staticmethod def get_number_of_mrnas(ins: Biomarker) -> int: """Gets the number of genes in this Biomarker""" @@ -110,7 +122,7 @@ class BiomarkerSimpleUpdateSerializer(serializers.ModelSerializer): """ class Meta: model = Biomarker - fields = ['name', 'description'] + fields = ['name', 'description', 'tissue'] class BiomarkerSerializer(WritableNestedModelSerializer): @@ -128,13 +140,17 @@ class BiomarkerSerializer(WritableNestedModelSerializer): methylations = MethylationIdentifierSerializer(many=True, required=False) origin = serializers.IntegerField(required=False) state = serializers.IntegerField(required=False) - tag = TagSerializer(required=False) class Meta: model = Biomarker exclude = ['user'] + def to_representation(self, instance): + data = super().to_representation(instance) + data['tissue'] = SimpleTissueSerializer(instance.tissue).data if instance.tissue else None + return data + @staticmethod def get_number_of_mrnas(ins: Biomarker) -> int: """Gets the number of genes in this Biomarker""" @@ -167,7 +183,7 @@ def get_was_already_used(ins: Biomarker) -> bool: This avoids the user to edit a Biomarker that was already used and generate inconsistencies. """ return ins.was_already_used - + class BiomarkerFromCorrelationAnalysisSerializer(serializers.Serializer): """ diff --git a/src/datasets_synchronization/admin.py b/src/datasets_synchronization/admin.py index 670b8c4c..88df1849 100644 --- a/src/datasets_synchronization/admin.py +++ b/src/datasets_synchronization/admin.py @@ -5,7 +5,7 @@ class CGDSStudyAdmin(admin.ModelAdmin): list_display = ('name', 'description', 'version', 'date_last_synchronization', 'state') - list_filter = ('state',) + list_filter = ('state', 'tissue') search_fields = ('name', 'description') def delete_queryset(self, request, queryset): @@ -42,9 +42,9 @@ def delete_queryset(self, request, queryset): 'mirna_dataset__name', 'mrna_dataset__name') - class SurvivalColumnsTupleAdmin(admin.ModelAdmin): """Useful for SurvivalColumnsTupleCGDSDataset and SurvivalColumnsTupleUserFile models.""" + @staticmethod @admin.display(description='CGDS Dataset') def dataset(obj: Union[SurvivalColumnsTupleCGDSDataset, SurvivalColumnsTupleUserFile]) -> str: @@ -53,6 +53,7 @@ def dataset(obj: Union[SurvivalColumnsTupleCGDSDataset, SurvivalColumnsTupleUser list_display = ('pk', 'dataset', 'time_column', 'event_column') search_fields = ('time_column', 'event_column') + # IMPORTANT: these models should be managed in the CGDS Panel in the frontend! admin.site.register(CGDSStudy, CGDSStudyAdmin) admin.site.register(CGDSDataset, CGDSDatasetAdmin) diff --git a/src/datasets_synchronization/migrations/0036_alter_cgdsstudy_clinical_patient_dataset_and_more.py b/src/datasets_synchronization/migrations/0036_alter_cgdsstudy_clinical_patient_dataset_and_more.py new file mode 100644 index 00000000..189242c2 --- /dev/null +++ b/src/datasets_synchronization/migrations/0036_alter_cgdsstudy_clinical_patient_dataset_and_more.py @@ -0,0 +1,80 @@ +# Generated by Django 4.2.19 on 2026-01-14 21:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("datasets_synchronization", "0035_auto_20230922_2356"), + ] + + operations = [ + migrations.AlterField( + model_name="cgdsstudy", + name="clinical_patient_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_clinical_patient_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="cgdsstudy", + name="clinical_sample_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_clinical_sample_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="cgdsstudy", + name="cna_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_cna_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="cgdsstudy", + name="methylation_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_methylation_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="cgdsstudy", + name="mirna_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_mirna_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + migrations.AlterField( + model_name="cgdsstudy", + name="mrna_dataset", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cgds_studies_as_mrna_dataset", + to="datasets_synchronization.cgdsdataset", + ), + ), + ] diff --git a/src/datasets_synchronization/migrations/0037_cgdsstudy_tissue.py b/src/datasets_synchronization/migrations/0037_cgdsstudy_tissue.py new file mode 100644 index 00000000..2d916279 --- /dev/null +++ b/src/datasets_synchronization/migrations/0037_cgdsstudy_tissue.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.19 on 2026-02-23 00:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0001_initial"), + ( + "datasets_synchronization", + "0036_alter_cgdsstudy_clinical_patient_dataset_and_more", + ), + ] + + operations = [ + migrations.AddField( + model_name="cgdsstudy", + name="tissue", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tissues.tissue", + ), + ), + ] diff --git a/src/datasets_synchronization/migrations/0038_remove_cgdsstudy_tissue_cgdsstudy_tissues.py b/src/datasets_synchronization/migrations/0038_remove_cgdsstudy_tissue_cgdsstudy_tissues.py new file mode 100644 index 00000000..b004849a --- /dev/null +++ b/src/datasets_synchronization/migrations/0038_remove_cgdsstudy_tissue_cgdsstudy_tissues.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.19 on 2026-03-05 22:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0002_tissue_code_and_initial_data"), + ("datasets_synchronization", "0037_cgdsstudy_tissue"), + ] + + operations = [ + migrations.RemoveField( + model_name="cgdsstudy", + name="tissue", + ), + migrations.AddField( + model_name="cgdsstudy", + name="tissues", + field=models.ManyToManyField(blank=True, to="tissues.tissue"), + ), + ] diff --git a/src/datasets_synchronization/migrations/0039_remove_cgdsstudy_tissues_cgdsstudy_tissues.py b/src/datasets_synchronization/migrations/0039_remove_cgdsstudy_tissues_cgdsstudy_tissues.py new file mode 100644 index 00000000..718a08d3 --- /dev/null +++ b/src/datasets_synchronization/migrations/0039_remove_cgdsstudy_tissues_cgdsstudy_tissues.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.19 on 2026-06-23 04:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0002_tissue_code_and_initial_data"), + ("datasets_synchronization", "0038_remove_cgdsstudy_tissue_cgdsstudy_tissues"), + ] + + operations = [ + migrations.RemoveField( + model_name="cgdsstudy", + name="tissues", + ), + migrations.AddField( + model_name="cgdsstudy", + name="tissues", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tissues.tissue", + ), + ), + ] diff --git a/src/datasets_synchronization/migrations/0040_rename_tissues_cgdsstudy_tissue.py b/src/datasets_synchronization/migrations/0040_rename_tissues_cgdsstudy_tissue.py new file mode 100644 index 00000000..e2fb07d9 --- /dev/null +++ b/src/datasets_synchronization/migrations/0040_rename_tissues_cgdsstudy_tissue.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2026-07-02 22:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("datasets_synchronization", "0039_remove_cgdsstudy_tissues_cgdsstudy_tissues"), + ] + + operations = [ + migrations.RenameField( + model_name="cgdsstudy", + old_name="tissues", + new_name="tissue", + ), + ] diff --git a/src/datasets_synchronization/models.py b/src/datasets_synchronization/models.py index ebf80f1d..1634a965 100644 --- a/src/datasets_synchronization/models.py +++ b/src/datasets_synchronization/models.py @@ -10,6 +10,7 @@ from common.methylation import MethylationPlatform from feature_selection.models import TrainedModel from statistical_properties.models import StatisticalValidation +from tissues.models import Tissue from user_files.models import UserFile from user_files.models_choices import FileType from pandas import DataFrame @@ -80,38 +81,41 @@ class CGDSDataset(models.Model): @property def file_type(self) -> FileType: - if hasattr(self, 'mrna_dataset'): + study = self.study + + if study and study.mrna_dataset_id == self.pk: return FileType.MRNA - elif hasattr(self, 'mirna_dataset'): + elif study and study.mirna_dataset_id == self.pk: return FileType.MIRNA - elif hasattr(self, 'cna_dataset'): + elif study and study.cna_dataset_id == self.pk: return FileType.CNA - elif hasattr(self, 'methylation_dataset'): + elif study and study.methylation_dataset_id == self.pk: return FileType.METHYLATION return FileType.CLINICAL - def __get_reverse_study(self) -> Optional['CGDSDataset']: + def __get_reverse_study(self) -> Optional['CGDSStudy']: """Gets the related study model's name""" - if hasattr(self, 'mrna_dataset'): - return cast(Optional['CGDSDataset'], self.mrna_dataset) - elif hasattr(self, 'mirna_dataset'): - return cast(Optional['CGDSDataset'], self.mirna_dataset) - elif hasattr(self, 'cna_dataset'): - return cast(Optional['CGDSDataset'], self.cna_dataset) - elif hasattr(self, 'methylation_dataset'): - return cast(Optional['CGDSDataset'], self.methylation_dataset) - elif hasattr(self, 'clinical_patient_dataset'): - return cast(Optional['CGDSDataset'], self.clinical_patient_dataset) - elif hasattr(self, 'clinical_sample_dataset'): - return cast(Optional['CGDSDataset'], self.clinical_sample_dataset) + if hasattr(self, 'cgds_studies_as_mrna_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_mrna_dataset) + elif hasattr(self, 'cgds_studies_as_mirna_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_mirna_dataset) + elif hasattr(self, 'cgds_studies_as_cna_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_cna_dataset) + elif hasattr(self, 'cgds_studies_as_methylation_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_methylation_dataset) + elif hasattr(self, 'cgds_studies_as_clinical_patient_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_clinical_patient_dataset) + elif hasattr(self, 'cgds_studies_as_clinical_sample_dataset'): + return cast(Optional['CGDSStudy'], self.cgds_studies_as_clinical_sample_dataset) + return None @property - def study(self) -> Optional['CGDSDataset']: + def study(self) -> Optional['CGDSStudy']: return self.__get_reverse_study() def __str__(self) -> str: study_name = self.study.name if self.study else '-' - return f'File: {self.file_path} | Col: {self.mongo_collection_name} | Assigned to study: {study_name}' + return f'PK: {self.pk} | File: {self.file_path} | Col: {self.mongo_collection_name} | Assigned to study: {study_name}' def __compute_number_of_row_and_samples_and_save(self) -> None: """ @@ -250,43 +254,44 @@ class CGDSStudy(models.Model): on_delete=models.SET_NULL, blank=True, null=True, - related_name='mrna_dataset' + related_name='cgds_studies_as_mrna_dataset' ) mirna_dataset = models.OneToOneField( CGDSDataset, on_delete=models.SET_NULL, blank=True, null=True, - related_name='mirna_dataset' + related_name='cgds_studies_as_mirna_dataset' ) cna_dataset = models.OneToOneField( CGDSDataset, on_delete=models.SET_NULL, blank=True, null=True, - related_name='cna_dataset' + related_name='cgds_studies_as_cna_dataset' ) methylation_dataset = models.OneToOneField( CGDSDataset, on_delete=models.SET_NULL, blank=True, null=True, - related_name='methylation_dataset' + related_name='cgds_studies_as_methylation_dataset' ) clinical_patient_dataset = models.OneToOneField( CGDSDataset, on_delete=models.SET_NULL, blank=True, null=True, - related_name='clinical_patient_dataset' + related_name='cgds_studies_as_clinical_patient_dataset' ) clinical_sample_dataset = models.OneToOneField( CGDSDataset, on_delete=models.SET_NULL, blank=True, null=True, - related_name='clinical_sample_dataset' + related_name='cgds_studies_as_clinical_sample_dataset' ) + tissue = models.ForeignKey(Tissue, on_delete=models.SET_NULL, default=None, blank=True, null=True) task_id: Optional[str] = models.CharField(max_length=100, blank=True, null=True) # Celery Task ID def __str__(self) -> str: diff --git a/src/datasets_synchronization/serializers.py b/src/datasets_synchronization/serializers.py index 26bd47fe..572c3e50 100644 --- a/src/datasets_synchronization/serializers.py +++ b/src/datasets_synchronization/serializers.py @@ -9,6 +9,7 @@ from .enums import CreateCGDSStudyResponseCode from .models import CGDSStudy, CGDSDataset, SurvivalColumnsTupleCGDSDataset from django.db.models import Q +from tissues.serializers import TissueSerializer class CGDSDatasetSerializer(serializers.ModelSerializer): @@ -28,7 +29,7 @@ class Meta: fields = ['id', 'time_column', 'event_column'] def get_fields(self, *args, **kwargs): - fields = super(SurvivalColumnsTupleCGDSSimpleSerializer, self).get_fields(*args, **kwargs) + fields = super(SurvivalColumnsTupleCGDSSimpleSerializer, self).get_fields() request = self.context.get('request', None) if request and getattr(request, 'method', None) == "POST": fields['id'].required = False @@ -123,9 +124,9 @@ def __check_collection_name(mongo_collection_name: str, editing_cgds_dataset_id: }) def __update_cgds_dataset( - self, - cgds_dataset_instance: CGDSDataset, - validated_data_pop + self, + cgds_dataset_instance: CGDSDataset, + validated_data_pop ) -> Optional[CGDSDataset]: """ Updates a CGDSDataset instance from a request data @@ -186,10 +187,9 @@ def __update_survival_columns(cgds_dataset_instance: CGDSDataset, validated_data # If there's an existing id, updates the element if 'id' in survival_column: try: - survival_column_obj: SurvivalColumnsTupleCGDSDataset = SurvivalColumnsTupleCGDSDataset. \ - objects.get( - pk=survival_column['id'] - ) + survival_column_obj: SurvivalColumnsTupleCGDSDataset = SurvivalColumnsTupleCGDSDataset.objects.get( + pk=survival_column['id'] + ) survival_column_obj.time_column = survival_column['time_column'] survival_column_obj.event_column = survival_column['event_column'] survival_column_obj.save() @@ -225,9 +225,8 @@ def create(self, validated_data) -> Optional[CGDSStudy]: methylation_dataset = self.__create_cgds_dataset(validated_data.pop('methylation_dataset')) clinical_patient_dataset = self.__create_cgds_dataset(validated_data.pop('clinical_patient_dataset')) clinical_sample_dataset = self.__create_cgds_dataset(validated_data.pop('clinical_sample_dataset')) - # Creates the CGDSStudy - return CGDSStudy.objects.create( + cgds_study = CGDSStudy.objects.create( mrna_dataset=mrna_dataset, mirna_dataset=mirna_dataset, cna_dataset=cna_dataset, @@ -236,6 +235,7 @@ def create(self, validated_data) -> Optional[CGDSStudy]: clinical_sample_dataset=clinical_sample_dataset, **validated_data ) + return cgds_study @staticmethod def __set_clinical_datasets_to_existing_experiments(cgds_study: CGDSStudy): @@ -263,25 +263,16 @@ def update(self, instance: CGDSStudy, validated_data): """ # Updates CGDSDatasets from request data with transaction.atomic(): - mrna_dataset = self.__update_cgds_dataset(instance.mrna_dataset, validated_data.pop('mrna_dataset')) - mirna_dataset = self.__update_cgds_dataset(instance.mirna_dataset, validated_data.pop('mirna_dataset')) - cna_dataset = self.__update_cgds_dataset(instance.cna_dataset, validated_data.pop('cna_dataset')) - methylation_dataset = self.__update_cgds_dataset( - instance.methylation_dataset, - validated_data.pop('methylation_dataset') - ) + mrna_dataset = self.__update_cgds_dataset(instance.mrna_dataset, validated_data.pop('mrna_dataset')) if 'mrna_dataset' in validated_data else instance.mrna_dataset + mirna_dataset = self.__update_cgds_dataset(instance.mirna_dataset, validated_data.pop('mirna_dataset')) if 'mirna_dataset' in validated_data else instance.mirna_dataset + cna_dataset = self.__update_cgds_dataset(instance.cna_dataset, validated_data.pop('cna_dataset')) if 'cna_dataset' in validated_data else instance.cna_dataset + methylation_dataset = self.__update_cgds_dataset(instance.methylation_dataset, validated_data.pop('methylation_dataset')) if 'methylation_dataset' in validated_data else instance.methylation_dataset old_clinical_patient_dataset = instance.clinical_patient_dataset old_clinical_sample_dataset = instance.clinical_sample_dataset - clinical_patient_dataset = self.__update_cgds_dataset( - instance.clinical_patient_dataset, - validated_data.pop('clinical_patient_dataset') - ) - clinical_sample_dataset = self.__update_cgds_dataset( - instance.clinical_sample_dataset, - validated_data.pop('clinical_sample_dataset') - ) + clinical_patient_dataset = self.__update_cgds_dataset(instance.clinical_patient_dataset, validated_data.pop('clinical_patient_dataset')) if 'clinical_patient_dataset' in validated_data else instance.clinical_patient_dataset + clinical_sample_dataset = self.__update_cgds_dataset(instance.clinical_sample_dataset, validated_data.pop('clinical_sample_dataset')) if 'clinical_sample_dataset' in validated_data else instance.clinical_sample_dataset # If it has been created clinical data, it's needed to link to existing experiments referencing to this # CGDSStudy @@ -303,27 +294,45 @@ def update(self, instance: CGDSStudy, validated_data): instance.clinical_patient_dataset = clinical_patient_dataset instance.clinical_sample_dataset = clinical_sample_dataset + # Updates tissue if provided + if 'tissue' in validated_data: + instance.tissue = validated_data['tissue'] + # Saves new changes and returns instance instance.save() return instance class SimpleCGDSDatasetSerializer(serializers.ModelSerializer): + """CGDSDataset serializer with few fields for list views.""" + name = serializers.SerializerMethodField(method_name='get_name') + description = serializers.SerializerMethodField(method_name='get_description') + version = serializers.SerializerMethodField(method_name='get_version') + tissue = serializers.SerializerMethodField(method_name='get_tissue') + file_obj = serializers.SerializerMethodField(method_name='get_file_obj') + class Meta: model = CGDSDataset - fields = [] - - def to_representation(self, instance): - # Gets the file content for user_file - data = super(SimpleCGDSDatasetSerializer, self).to_representation(instance) - - # Serialize the study - study = instance.study - data['name'] = study.name - data['description'] = study.description - data['version'] = study.version - data['date_last_synchronization'] = instance.date_last_synchronization - data['file_type'] = instance.file_type - data['file_obj'] = None - - return data + fields = ['id', 'name', 'description', 'version', 'date_last_synchronization', 'file_type', 'file_obj', + 'tissue'] + + @staticmethod + def get_file_obj(_instance: CGDSDataset): + """Returns None to avoid sending the file in list views.""" + return None + + @staticmethod + def get_name(instance: CGDSDataset): + return instance.study.name if instance.study else None + + @staticmethod + def get_description(instance: CGDSDataset): + return instance.study.description if instance.study else None + + @staticmethod + def get_version(instance: CGDSDataset): + return instance.study.version if instance.study else None + + @staticmethod + def get_tissue(instance: CGDSDataset): + return TissueSerializer(instance.study.tissue).data if instance.study and instance.study.tissue else None diff --git a/src/datasets_synchronization/urls.py b/src/datasets_synchronization/urls.py index 595a6b54..e5f71a5d 100644 --- a/src/datasets_synchronization/urls.py +++ b/src/datasets_synchronization/urls.py @@ -7,6 +7,9 @@ # CGDS Studies path('studies', views.CGDSStudyList.as_view(), name='cgds_studies'), path('studies//', views.CGDSStudyDetail.as_view()), + # CGDS Datasets + path('cgds-dataset-clinical-attributes/', views.CGDSDatasetClinicalAttributes.as_view(), name='cgds_dataset_clinical_attributes'), + path('cgds-dataset-clinical-attributes//', views.CGDSDatasetClinicalAttributes.as_view()), # Synchronization path('sync', views.SyncCGDSStudy.as_view(), name='sync_cgds_study'), path('stop-sync', views.StopCGDSSync.as_view(), name='stop_cgds_study_sync') diff --git a/src/datasets_synchronization/views.py b/src/datasets_synchronization/views.py index 2818b2c6..abacb9a2 100644 --- a/src/datasets_synchronization/views.py +++ b/src/datasets_synchronization/views.py @@ -11,11 +11,14 @@ from common.pagination import StandardResultsSetPagination from common.response import ResponseStatus from .enums import SyncCGDSStudyResponseCode, SyncStrategy -from .models import CGDSStudy, CGDSDatasetSynchronizationState, CGDSStudySynchronizationState +from .models import CGDSStudy, CGDSDatasetSynchronizationState, CGDSStudySynchronizationState, CGDSDataset from rest_framework import generics, permissions, filters +from django_filters.rest_framework import DjangoFilterBackend from user_files.models_choices import FileType from .serializers import CGDSStudySerializer -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404 +from api_service.utils import get_cgds_dataset +from user_files.models_choices import FileType @login_required @@ -80,7 +83,8 @@ def get_queryset(self): serializer_class = CGDSStudySerializer permission_classes = [permissions.IsAuthenticated] pagination_class = StandardResultsSetPagination - filter_backends = [filters.OrderingFilter, filters.SearchFilter] + filter_backends = [filters.OrderingFilter, filters.SearchFilter, DjangoFilterBackend] + filterset_fields = ['tissue'] search_fields = ['name', 'description'] ordering_fields = '__all__' @@ -214,3 +218,17 @@ def get(request: Request): # Formats to JSON the ResponseStatus object return Response(response) + + +class CGDSDatasetClinicalAttributes(APIView): + """REST endpoint: list for CGDSDataset header.""" + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def get(request, pk: int): + """Gets the clinical attributes of a CGDSDataset.""" + cgds_study = get_object_or_404(CGDSStudy, pk=pk) + # Gets the corresponding Study's Dataset + cgds_dataset = get_cgds_dataset(cgds_study, FileType.CLINICAL) + list_of_samples = cgds_dataset.get_column_names() + return Response(list_of_samples) diff --git a/src/differential_expression/__init__.py b/src/differential_expression/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/differential_expression/admin.py b/src/differential_expression/admin.py new file mode 100644 index 00000000..92757125 --- /dev/null +++ b/src/differential_expression/admin.py @@ -0,0 +1,78 @@ +from django.contrib import admin +from .models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentResult +) + + +@admin.register(DifferentialExpressionExperiment) +class DifferentialExpressionExperimentAdmin(admin.ModelAdmin): + """Admin configuration for DifferentialExpressionExperiment model.""" + + list_display = ( + 'id', 'name', 'user', 'state', 'clinical_attribute', 'tool', + 'threshold_percentile', 'threshold', 'top', 'execution_time', 'created_at' + ) + list_filter = ( + 'state', 'is_public', 'clinical_attribute', 'created_at', 'tissue' + ) + search_fields = ('name', 'description', 'user__username', 'clinical_attribute') + readonly_fields = ( + 'id', 'task_id', 'execution_time', 'attempt', 'created_at', 'updated_at', + 'get_results_count' + ) + filter_horizontal = ('shared_institutions', 'shared_users') + + fieldsets = ( + ('Basic Information', { + 'fields': ('id', 'name', 'description', 'user') + }), + ('Data Sources', { + 'fields': ('clinical_source', 'mrna_source') + }), + ('Analysis Parameters', { + 'fields': ('clinical_attribute', 'tool', 'threshold_percentile', 'threshold', 'top') + }), + ('Execution Information', { + 'fields': ('state', 'task_id', 'execution_time', 'attempt', 'created_at', 'updated_at'), + 'classes': ('collapse',) + }), + ('Results Statistics', { + 'fields': ('get_results_count',), + 'classes': ('collapse',) + }), + ('Sharing', { + 'fields': ('is_public', 'shared_institutions', 'shared_users', 'tissue'), + 'classes': ('collapse',) + }), + ) + + def get_results_count(self, obj): + """Returns the total number of results.""" + try: + return obj.results.count() + except: + return 0 + get_results_count.short_description = 'Total Results' + + +@admin.register(DifferentialExpressionExperimentResult) +class DifferentialExpressionExperimentResultAdmin(admin.ModelAdmin): + """Admin configuration for DifferentialExpressionExperimentResult model.""" + + list_display = ( + 'id', 'experiment', 'gene', 'adj_p_val', 'log_fc', + 'p_value', 'ave_expr' + ) + list_filter = ('experiment__state', 'experiment__user') + search_fields = ('gene', 'experiment__name', 'experiment__user__username') + readonly_fields = ('id',) + + fieldsets = ( + ('Basic Information', { + 'fields': ('id', 'experiment', 'gene') + }), + ('Statistical Results', { + 'fields': ('p_value', 'adj_p_val', 'log_fc', 'ave_expr', 't_statistic', 'b_statistic') + }), + ) diff --git a/src/differential_expression/apps.py b/src/differential_expression/apps.py new file mode 100644 index 00000000..854cf485 --- /dev/null +++ b/src/differential_expression/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DifferentialExpressionConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'differential_expression' diff --git a/src/differential_expression/migrations/0001_initial.py b/src/differential_expression/migrations/0001_initial.py new file mode 100644 index 00000000..c098fcec --- /dev/null +++ b/src/differential_expression/migrations/0001_initial.py @@ -0,0 +1,62 @@ +# Generated by Django 4.2.19 on 2026-01-29 23:38 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('institutions', '0005_alter_institution_options_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('api_service', '0062_alter_experiment_clinical_source_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='DifferentialExpressionExperiment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=300)), + ('description', models.TextField(blank=True, null=True)), + ('clinical_attribute', models.CharField(max_length=100)), + ('tool', models.CharField(choices=[('DESEQ', 'Deseq'), ('LIMMA', 'Limma')], default='DESEQ', help_text='Tool to use for differential expression analysis', max_length=10)), + ('threshold_percentile', models.FloatField(default=0.15)), + ('threshold', models.FloatField(default=0.0001)), + ('top', models.IntegerField(default=100, help_text='Number of significant results to keep (max 1000)')), + ('execution_time', models.FloatField(blank=True, default=0.0, help_text='Execution time in seconds', null=True)), + ('task_id', models.CharField(blank=True, help_text='Celery Task ID', max_length=100, null=True)), + ('attempt', models.PositiveSmallIntegerField(default=0, help_text='Number of attempts to prevent a buggy experiment running forever')), + ('state', models.IntegerField(choices=[(1, 'Completed'), (2, 'Finished With Error'), (3, 'In Process'), (4, 'Waiting For Queue'), (5, 'No Samples In Common'), (6, 'Stopping'), (7, 'Stopped'), (8, 'Reached Attempts Limit'), (9, 'No Features Found'), (10, 'Empty Dataset'), (11, 'Timeout Exceeded')], default=4, help_text='Current state of the differential expression experiment')), + ('created_at', models.DateTimeField(auto_now_add=True, help_text='When the experiment was created', null=True)), + ('updated_at', models.DateTimeField(auto_now=True, help_text='When the experiment was last updated')), + ('is_public', models.BooleanField(default=False)), + ('clinical_source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='differential_expression_experiments_as_clinical', to='api_service.experimentclinicalsource')), + ('mrna_source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='differential_expression_experiments_as_mrna', to='api_service.experimentsource')), + ('shared_institutions', models.ManyToManyField(blank=True, related_name='shared_differential_expression', to='institutions.institution')), + ('shared_users', models.ManyToManyField(blank=True, related_name='shared_users_differential_expression', to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='DifferentialExpressionExperimentResult', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('gene', models.CharField(help_text='Gene identifier', max_length=100)), + ('ave_expr', models.FloatField(help_text='Average expression level')), + ('p_value', models.FloatField(help_text='P-value from statistical test')), + ('adj_p_val', models.FloatField(help_text='Adjusted P-value (FDR corrected)')), + ('log_fc', models.FloatField(help_text='Log fold change')), + ('t_statistic', models.FloatField(help_text='t-statistic from the test')), + ('b_statistic', models.FloatField(help_text='B-statistic (log-odds of differential expression)')), + ('experiment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results', to='differential_expression.differentialexpressionexperiment')), + ], + options={ + 'indexes': [models.Index(fields=['experiment', 'adj_p_val'], name='differentia_experim_c0aff3_idx'), models.Index(fields=['experiment', 'log_fc'], name='differentia_experim_084601_idx'), models.Index(fields=['gene'], name='differentia_gene_7c8c66_idx')], + 'unique_together': {('experiment', 'gene')}, + }, + ), + ] diff --git a/src/differential_expression/migrations/0002_differentialexpressionexperiment_tissues.py b/src/differential_expression/migrations/0002_differentialexpressionexperiment_tissues.py new file mode 100644 index 00000000..90547151 --- /dev/null +++ b/src/differential_expression/migrations/0002_differentialexpressionexperiment_tissues.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.19 on 2026-06-23 03:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("tissues", "0002_tissue_code_and_initial_data"), + ("differential_expression", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="differentialexpressionexperiment", + name="tissues", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="tissues.tissue", + ), + ), + ] diff --git a/src/differential_expression/migrations/0003_rename_tissues_differentialexpressionexperiment_tissue.py b/src/differential_expression/migrations/0003_rename_tissues_differentialexpressionexperiment_tissue.py new file mode 100644 index 00000000..8dce9b68 --- /dev/null +++ b/src/differential_expression/migrations/0003_rename_tissues_differentialexpressionexperiment_tissue.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.19 on 2026-07-02 22:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("differential_expression", "0002_differentialexpressionexperiment_tissues"), + ] + + operations = [ + migrations.RenameField( + model_name="differentialexpressionexperiment", + old_name="tissues", + new_name="tissue", + ), + ] diff --git a/src/differential_expression/migrations/__init__.py b/src/differential_expression/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/differential_expression/models.py b/src/differential_expression/models.py new file mode 100644 index 00000000..f803ed48 --- /dev/null +++ b/src/differential_expression/models.py @@ -0,0 +1,236 @@ +from django.db import models +from django.contrib.auth import get_user_model +from institutions.models import Institution +from django.contrib.auth.models import User +from tissues.models import Tissue +import pandas as pd +import math +from django.db.models import Q, QuerySet + +from api_service.websocket_functions import send_update_differential_expression_experiments_command +from api_service.models import ExperimentSource, ExperimentClinicalSource + + +class DifferentialExpressionExperimentState(models.IntegerChoices): + """All the possible states of a Differential Expression Experiment.""" + COMPLETED = 1 + FINISHED_WITH_ERROR = 2 + IN_PROCESS = 3 + WAITING_FOR_QUEUE = 4 + NO_SAMPLES_IN_COMMON = 5 + STOPPING = 6 + STOPPED = 7 + REACHED_ATTEMPTS_LIMIT = 8 + NO_FEATURES_FOUND = 9 + EMPTY_DATASET = 10 + TIMEOUT_EXCEEDED = 11 + + +class DifferentialExpressionTool(models.TextChoices): + """Tool choices for differential expression analysis.""" + DESEQ = 'DESEQ' + LIMMA = 'LIMMA' + + +class DifferentialExpressionExperiment(models.Model): + """ + Model to create and manage differential expression data. + """ + results: QuerySet['DifferentialExpressionExperimentResult'] + + name = models.CharField(max_length=300) + description = models.TextField(blank=True, null=True) + + # Clinical and mRNA sources + # These are used to link the experiment to the clinical and mRNA data sources + clinical_source = models.ForeignKey( + 'api_service.ExperimentClinicalSource', + on_delete=models.CASCADE, + null=False, + blank=False, + related_name='differential_expression_experiments_as_clinical' + ) + + mrna_source = models.ForeignKey( + 'api_service.ExperimentSource', + on_delete=models.CASCADE, + null=False, + blank=False, + related_name='differential_expression_experiments_as_mrna' + ) + + clinical_attribute = models.CharField(max_length=100, blank=False, null=False) + + tool = models.CharField( + max_length=10, + choices=DifferentialExpressionTool.choices, + default=DifferentialExpressionTool.DESEQ, + blank=False, + null=False, + help_text='Tool to use for differential expression analysis' + ) + + threshold_percentile = models.FloatField(default=0.15, blank=False, null=False) + + threshold = models.FloatField(default=0.0001, blank=False, null=False) + + top = models.IntegerField( + default=100, + blank=False, + null=False, + help_text='Number of significant results to keep (max 1000)' + ) + + # Celery task related fields + # This is used to track the execution of the task and its state + execution_time = models.FloatField(default=0.0, blank=True, null=True, help_text='Execution time in seconds') + task_id = models.CharField(max_length=100, blank=True, null=True, help_text='Celery Task ID') + attempt = models.PositiveSmallIntegerField(default=0, help_text='Number of attempts to prevent a buggy experiment ' + 'running forever') + state = models.IntegerField( + choices=DifferentialExpressionExperimentState.choices, + default=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE, + help_text='Current state of the differential expression experiment' + ) + + # Timestamp fields + created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True, + help_text='When the experiment was created') + updated_at = models.DateTimeField(auto_now=True, help_text='When the experiment was last updated') + + # User and sharing information + # This is used to track the user who created the experiment and to share it with other users or institutions + user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) + is_public = models.BooleanField(blank=False, null=False, default=False) + shared_institutions = models.ManyToManyField(Institution, related_name='shared_differential_expression', blank=True) + shared_users = models.ManyToManyField(User, blank=True, + related_name='shared_users_differential_expression') + tissue = models.ForeignKey(Tissue, on_delete=models.SET_NULL, default=None, blank=True, null=True) + + def __str__(self): + return f"Differential Expression Experiment: {self.name}" + + def save(self, *args, **kwargs): + """ + Every time the experiment status changes, uses websockets to update state in the frontend. + """ + if not self.name: + raise ValueError("Experiment name cannot be empty.") + + super().save(*args, **kwargs) + + # Sends a websockets message to update the experiment state in the frontend + send_update_differential_expression_experiments_command(self.user.id) + + def save_results(self, dataframe): + """ + Save the differential expression DataFrame results. + @param dataframe: pandas DataFrame with differential expression results. + """ + # Clear existing results + self.results.all().delete() + + # Create new result records + results_to_create = [] + for gene_name, row in dataframe.iterrows(): + # Handle NaN values by replacing them with appropriate defaults + def safe_float(value, default=0.0): + """Convert value to float, handling NaN and inf values.""" + try: + if pd.isna(value) or math.isinf(float(value)): + return default + return float(value) + except (ValueError, TypeError): + return default + + result = DifferentialExpressionExperimentResult( + experiment=self, + gene=str(gene_name), # The gene identifier from the DataFrame index + ave_expr=safe_float(row.get('AveExpr', 0.0), 0.0), + p_value=safe_float(row.get('P.Value', 1.0), 1.0), + adj_p_val=safe_float(row.get('adj.P.Val', 1.0), 1.0), + log_fc=safe_float(row.get('logFC', 0.0), 0.0), + t_statistic=safe_float(row.get('t', 0.0), 0.0), + b_statistic=safe_float(row.get('B', 0.0), 0.0) + ) + results_to_create.append(result) + + # Bulk create for efficiency + DifferentialExpressionExperimentResult.objects.bulk_create(results_to_create) + + def get_results_dataframe(self): + """ + Retrieve results as a pandas DataFrame. + """ + results = self.results.all() + if not results.exists(): + return None + + data = [{ + 'gene': result.gene, + 'AveExpr': result.ave_expr, + 'P.Value': result.p_value, + 'adj.P.Val': result.adj_p_val, + 'logFC': result.log_fc, + 't': result.t_statistic, + 'B': result.b_statistic + } for result in results] + + df = pd.DataFrame(data) + df.set_index('gene', inplace=True) + return df + + def get_significant_genes(self, p_value_threshold=0.05, log_fc_threshold=1.0): + """ + Get significantly differentially expressed genes. + @param p_value_threshold: Maximum adjusted p-value (default 0.05). + @param log_fc_threshold: Minimum absolute log fold change (default 1.0). + @return QuerySet of DifferentialExpressionExperimentResult objects meeting the criteria. + """ + + return self.results.filter( + Q(adj_p_val__lte=p_value_threshold) & + (Q(log_fc__gte=log_fc_threshold) | Q(log_fc__lte=-log_fc_threshold)) + ) + + def delete(self, *args, **kwargs): + """ + Deletes the instance and sends a websockets message to update state in the frontend + """ + user_id = self.user.id # Store user_id before deletion + super().delete(*args, **kwargs) + + # Sends a websockets message to update the experiment state in the frontend + send_update_differential_expression_experiments_command(user_id) + + +class DifferentialExpressionExperimentResult(models.Model): + """ + Model to store individual differential expression results for each gene. + """ + + experiment = models.ForeignKey( + 'DifferentialExpressionExperiment', + on_delete=models.CASCADE, + related_name='results' + ) + gene = models.CharField(max_length=100, help_text='Gene identifier') + ave_expr = models.FloatField(help_text='Average expression level') + p_value = models.FloatField(help_text='P-value from statistical test') + adj_p_val = models.FloatField(help_text='Adjusted P-value (FDR corrected)') + log_fc = models.FloatField(help_text='Log fold change') + t_statistic = models.FloatField(help_text='t-statistic from the test') + b_statistic = models.FloatField(help_text='B-statistic (log-odds of differential expression)') + + class Meta: + unique_together = ('experiment', 'gene') + indexes = [ + models.Index(fields=['experiment', 'adj_p_val']), + models.Index(fields=['experiment', 'log_fc']), + models.Index(fields=['gene']), + ] + + def __str__(self): + return f"{self.gene} - {self.experiment.name}" + + diff --git a/src/differential_expression/serializers.py b/src/differential_expression/serializers.py new file mode 100644 index 00000000..a0f51bbd --- /dev/null +++ b/src/differential_expression/serializers.py @@ -0,0 +1,146 @@ +from django.contrib.auth.models import User +from rest_framework import serializers + +from api_service.serializers import ExperimentSourceSerializer, ExperimentClinicalSourceSerializer +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentResult +) +from institutions.models import Institution + + +class UserSimpleForDiffExpExperiments(serializers.ModelSerializer): + """Simple serializer of User model for some differential expression analysis serializers.""" + + class Meta: + model = User + fields = ['id', 'username', 'first_name', 'last_name', 'email'] + read_only_fields = ['id', 'username', 'first_name', 'last_name', 'email'] + + +class InstitutionSimpleForDiffExpExperiments(serializers.ModelSerializer): + """Simple serializer of Institution model for some differential expression analysis serializers.""" + + class Meta: + model = Institution + fields = ['id', 'name'] + read_only_fields = ['id', 'name'] + + +class DifferentialExpressionExperimentResultSerializer(serializers.ModelSerializer): + """ + Serializer for Differential Expression Experiment Results. + """ + class Meta: + model = DifferentialExpressionExperimentResult + fields = ['id', 'gene', 'ave_expr', 'p_value', 'adj_p_val', 'log_fc', 't_statistic', 'b_statistic'] + + +class DifferentialExpressionVolcanoPlotSerializer(serializers.ModelSerializer): + """ + Serializer for volcano plot visualization. + Returns minimal fields needed for plotting: id, label (gene name), log2FC, and pValue (adj.P.Val). + """ + label = serializers.CharField(source='gene', read_only=True) + log2FC = serializers.FloatField(source='log_fc', read_only=True) + pValue = serializers.FloatField(source='adj_p_val', read_only=True) + + class Meta: + model = DifferentialExpressionExperimentResult + fields = ['id', 'label', 'log2FC', 'pValue'] + +class SimpleTissueSerializer(serializers.Serializer): + """Lightweight serializer for Tissue model""" + id = serializers.IntegerField() + name = serializers.CharField() + code = serializers.CharField() + +class DifferentialExpressionExperimentListSerializer(serializers.ModelSerializer): + """ + Optimized serializer for differential expression experiments list view. + Returns essential fields for table display: id, user, name, description, created_at, + state, clinical_source, mrna_source, and is_public. + Uses compatible source serializers that match frontend DjangoExperimentSource interface. + """ + # Sources - using standard API serializers compatible with frontend interfaces + clinical_source = ExperimentClinicalSourceSerializer(read_only=True) + mrna_source = ExperimentSourceSerializer(read_only=True) + user = UserSimpleForDiffExpExperiments(read_only=True) + tissue = SimpleTissueSerializer(read_only=True) + + # State information + state_display = serializers.CharField(source='get_state_display', read_only=True) + + class Meta: + model = DifferentialExpressionExperiment + fields = [ + 'id', # Experiment ID + 'user', # User ID (compatible with frontend expectations) + 'name', # Experiment name + 'description', # Experiment description + 'created_at', # Creation date + 'state', # Experiment state + 'state_display', # Human-readable state + 'clinical_source', # Clinical data source (ExperimentClinicalSourceSerializer) + 'mrna_source', # mRNA data source (ExperimentSourceSerializer) + 'is_public', # Public visibility flag + 'tool', + 'tissue' + ] + read_only_fields = [ + 'id', 'created_at', 'state', 'state_display' + ] + + +class DifferentialExpressionExperimentSerializer(serializers.ModelSerializer): + """ + Serializer for Differential Expression Experiment (General view with all fields). + """ + user = UserSimpleForDiffExpExperiments(read_only=True) + clinical_source = ExperimentClinicalSourceSerializer(read_only=True) + mrna_source = ExperimentSourceSerializer(read_only=True) + + # Computed fields + has_results = serializers.SerializerMethodField(method_name='get_has_results') + results_count = serializers.SerializerMethodField(method_name='get_results_count') + significant_genes_count = serializers.SerializerMethodField(method_name='get_significant_genes_count') + state_display = serializers.CharField(source='get_state_display', read_only=True) + + class Meta: + model = DifferentialExpressionExperiment + fields = [ + 'id', 'name', 'description', 'user', 'clinical_source', 'mrna_source', + 'clinical_attribute', 'tool', 'threshold_percentile', 'threshold', 'top', 'state', 'state_display', + 'execution_time', 'created_at', 'updated_at', 'is_public', + 'has_results', 'results_count', 'significant_genes_count', 'tissue' + ] + read_only_fields = [ + 'id', 'execution_time', 'created_at', 'updated_at', + 'has_results', 'results_count', 'significant_genes_count' + ] + + @staticmethod + def get_has_results(obj: DifferentialExpressionExperiment) -> bool: + """Check if the experiment has results.""" + return obj.results.exists() + + @staticmethod + def get_results_count(obj: DifferentialExpressionExperiment) -> int: + """Get the total number of genes in results.""" + return obj.results.count() + + @staticmethod + def get_significant_genes_count(obj: DifferentialExpressionExperiment) -> int: + """Get the number of significant genes (adj.P.Val <= 0.05 and |logFC| >= 1.0).""" + return obj.get_significant_genes().count() + + +class DifferentialExpressionExperimentDetailSerializer(serializers.ModelSerializer): + """ + Detailed serializer for Differential Expression Experiment (Detail view). + Includes additional information like shared users and institutions. + """ + + class Meta:#DifferentialExpressionExperimentSerializer.Meta): + fields = '__all__' + model = DifferentialExpressionExperimentResult diff --git a/src/differential_expression/service.py b/src/differential_expression/service.py new file mode 100644 index 00000000..6480e209 --- /dev/null +++ b/src/differential_expression/service.py @@ -0,0 +1,359 @@ +from common.exceptions import EmptyDataset +from common.typing import AbortEvent +from differential_expression.models import DifferentialExpressionExperiment +import logging +import warnings +from itertools import combinations + +import numpy as np +import pandas as pd + +# Filter R warnings BEFORE importing rpy2 +warnings.filterwarnings('ignore', + message='Environment variable "XPC_SERVICE_NAME" redefined by R', + category=UserWarning) +warnings.filterwarnings('ignore', + message='Environment variable "R_SESSION_TMPDIR" redefined by R and overriding existing variable.', + category=UserWarning) + +import rpy2.robjects as robjects +from rpy2.robjects import pandas2ri +from rpy2.robjects.conversion import get_conversion, localconverter +from rpy2.robjects.pandas2ri import converter +from rpy2.robjects.packages import importr + +class DifferentialExpressionService: + def __init__(self, experiment: DifferentialExpressionExperiment, is_aborted: AbortEvent): + self.experiment = experiment + self.is_aborted = is_aborted + + def perform_differential_expression(self) -> pd.DataFrame: + clinical_df, mrna_df = self._process_datasets() + + sample_column = 'SAMPLE_ID' + if sample_column in clinical_df.columns: + common_samples = sorted(set(mrna_df.columns) & set(clinical_df[sample_column])) + + # Check if there are samples in common + if len(common_samples) == 0: + from differential_expression.models import DifferentialExpressionExperimentState + raise ValueError("NO_SAMPLES_IN_COMMON") + + df_RNAseq_filtered = mrna_df[common_samples] + df_clinical_filtered = clinical_df[clinical_df[sample_column].isin(common_samples)] + df_clinical_filtered = df_clinical_filtered.set_index(sample_column).reindex(common_samples).reset_index() + else: + df_RNAseq_filtered = mrna_df + df_clinical_filtered = clinical_df + + # Check if we have any features (genes) left after filtering + if df_RNAseq_filtered.empty or df_RNAseq_filtered.shape[0] == 0: + raise ValueError("NO_FEATURES_FOUND") + + try: + # Import R packages + limma = importr('limma') # limma for differential expression + stats = importr('stats') # stats for model matrix + base = importr('base') # base R functions + + # 1. Validation: Ensure the clinical attribute has at least two categories + # This is necessary because differential expression requires at least two groups to compare. + unique_values = df_clinical_filtered[self.experiment.clinical_attribute].dropna().unique() + if len(unique_values) < 2: + raise ValueError(f"At least two categories are required in '{self.experiment.clinical_attribute}'.") + + # 2. Group vector creation + # Converts the clinical attribute to a categorical variable (factor in R). + # This tells the model to treat the values as groups, not as numeric values. + group_values = df_clinical_filtered[self.experiment.clinical_attribute].astype(str).values + group_levels = sorted(np.unique(group_values)) # Get all unique group names sorted + group_dict = {k: i for i, k in enumerate(group_levels)} # Map group names to indices (not strictly needed) + group_factor = pd.Categorical(group_values, categories=group_levels) + + # 3. Conversion to R objects + # Converts the filtered log-expression data (Pandas DataFrame) to an R matrix. + with localconverter(get_conversion() + converter): + r_log_data = pandas2ri.py2rpy(df_RNAseq_filtered) + # Convert the group factor to an R factor vector + r_group = robjects.FactorVector(group_factor) + + # 4. Design matrix construction (no intercept) + # The design matrix encodes the group structure for the linear model. + # Using '~ 0 + group' means no intercept: each group gets its own column. + formula = robjects.Formula('~ 0 + group') + env = robjects.Environment() + env['group'] = r_group + r_design = stats.model_matrix(formula, env) + r_design.colnames = robjects.StrVector(group_levels) # Set column names to group names + + # 5. Linear model fitting with limma + # Fit the linear model to estimate mean expression for each gene in each group. + fit = limma.lmFit(r_log_data, r_design) + + # 6. Create all possible pairwise contrasts (all-vs-all) + # For each pair of groups, create a contrast expression like 'groupB - groupA'. + pares = list(combinations(group_levels, 2)) + contrastes = [f"{b} - {a}" for a, b in pares] + contrast_matrix = limma.makeContrasts( + contrasts=robjects.StrVector(contrastes), + levels=r_design + ) + + # 7. Apply contrasts and empirical Bayes moderation + # Apply the contrasts to the fitted model, then use eBayes to stabilize variance estimates. + fit2 = limma.contrasts_fit(fit, contrast_matrix) + fit2 = limma.eBayes(fit2) + + # 8. Extract results for the first contrast + # Get the table of differential expression results for the first contrast (logFC, p-value, adjusted p-value, etc.). + results = limma.topTable( + fit2, + coef=1, # First contrast + number=robjects.r('Inf'), # All genes + adjust_method="BH" # Benjamini-Hochberg adjustment + ) + + # Convert the R data frame to a Pandas DataFrame for further analysis in Python. + results_df = pandas2ri.rpy2py(results) + + # Return top genes sorted by adjusted p-value, keeping gene names as index + top_genes = results_df.nsmallest(self.experiment.top, 'adj.P.Val') + + return top_genes + + except Exception as e: + logging.error(f"Error occurred during differential expression analysis: {e}") + raise + + def _process_datasets(self): + """Process clinical and mRNA datasets for differential expression analysis.""" + clinical_df = self.experiment.clinical_source.get_df() + mrna_df = self.experiment.mrna_source.get_df() + + if clinical_df.empty or mrna_df.empty: + raise EmptyDataset("One or both datasets are empty.") + + data_processing_service = DataProcessingService( + clinical_df=clinical_df, + mrna_df=mrna_df, + clinical_attribute=self.experiment.clinical_attribute, + threshold_percentile=self.experiment.threshold_percentile, + threshold=self.experiment.threshold + ) + + return data_processing_service.process_datasets() + + +class DataProcessingService: + """ + Service for processing clinical and RNA-Seq datasets. + """ + + def __init__(self, + clinical_df: pd.DataFrame, + mrna_df: pd.DataFrame, + clinical_attribute: str, + threshold_percentile: float = 0.15, + threshold : float = 1e-4): + + self.clinical_df = clinical_df + self.mrna_df = mrna_df + self.clinical_attribute = clinical_attribute + self.threshold_percentile = threshold_percentile + self.threshold = threshold + + def validate_datasets(self): + """Validate the clinical and mRNA datasets. + + Raises: + EmptyDataset: If either dataset is empty. + """ + if self.clinical_df.empty: + raise EmptyDataset("Clinical dataset is empty.") + if self.mrna_df.empty: + raise EmptyDataset("mRNA dataset is empty.") + + def _process_clinical_data(self) -> None: + """Process the clinical dataset. + This method filters the clinical dataset to keep only the relevant columns, + removes rows with NA values in the clinical attribute, and ensures that + the clinical attribute is in uppercase if it is of string type. + """ + + ###### Procesar datos clínicos ##### + # Reset index para convertir PATIENT_ID de índice a columna + clinical_df_reset = self.clinical_df.reset_index() + + # Create sample dataframe with SAMPLE_ID and PATIENT_ID + df_clinical_sample = clinical_df_reset[['SAMPLE_ID', 'PATIENT_ID']] + + # Create patient dataframe with PATIENT_ID and clinical attribute, removing NA values + df_clinical_patient = clinical_df_reset[['PATIENT_ID', self.clinical_attribute]].dropna(subset=[self.clinical_attribute]) + + # Si la columna es de tipo texto, limpiar y convertir a mayúsculas + if df_clinical_patient[self.clinical_attribute].dtype == 'object': + # Eliminar espacios al inicio y al final, luego convertir a mayúsculas + df_clinical_patient[self.clinical_attribute] = df_clinical_patient[self.clinical_attribute].astype( + str).str.strip().str.upper() + + # Eliminar filas duplicadas + df_clinical_patient = df_clinical_patient.drop_duplicates() + + ##### Procesar datos de muestra ##### + # df_clinical_sample = self.clinical_df[['SAMPLE_ID', 'PATIENT_ID']] + + ##### Fusionar datos clínicos y de muestra ##### + df_clinical = pd.merge(df_clinical_sample, df_clinical_patient, on='PATIENT_ID', how='inner') + df_clinical = df_clinical[['SAMPLE_ID', self.clinical_attribute]] + + df_clinical = df_clinical.drop_duplicates() + df_clinical['SAMPLE_ID'] = df_clinical['SAMPLE_ID'].str.replace('-', '.') + + self.clinical_df = df_clinical + logging.info("Clinical data processed successfully.") + + + def _process_mrna_data(self) -> None: + """Process the mRNA dataset. + This method processes the mRNA dataset by renaming columns, removing duplicates, + filtering samples based on the clinical dataset, and cleaning up NA values. + """ + + mrna_dataset = self.mrna_df.copy() + + mrna_dataset.reset_index(inplace=True) + + # SOLUCIÓN: Reemplazar guiones por puntos en los nombres de las columnas sino no puede machear con SAMPLE_ID + columns_to_rename = {} + for col in mrna_dataset.columns: + if col not in 'Standard_Symbol': + columns_to_rename[col] = col.replace('-', '.') + + mrna_dataset = mrna_dataset.rename(columns=columns_to_rename) + + mrna_dataset = mrna_dataset.drop_duplicates(subset=['Standard_Symbol'], keep='first') + + # # Si Standard_Symbol está como índice, convertirlo a columna + # if mrna_dataset.index.name == 'Standard_Symbol' in str(mrna_dataset.index.name): + # mrna_dataset = mrna_dataset.reset_index() + + # Si Standard_Symbol no está como columna pero está en el índice + # if 'Standard_Symbol' not in mrna_dataset.columns and mrna_dataset.index.name is not None: + # mrna_dataset = mrna_dataset.reset_index() + # # Renombrar la primera columna a Standard_Symbol si es necesario + # if mrna_dataset.columns[0] != 'Standard_Symbol': + # mrna_dataset = mrna_dataset.rename(columns={mrna_dataset.columns[0]: 'Standard_Symbol'}) + + + # Eliminar duplicados manteniendo la primera ocurrencia + # mrna_dataset = self.mrna_df.drop_duplicates(subset=['Standard_Symbol'], keep='first') + + # Antes de la intersección, normalizar los SAMPLE_ID extrayendo solo la parte base + self.clinical_df['SAMPLE_ID'] = self.clinical_df['SAMPLE_ID'].str.rsplit('.', n=1).str[0] + + # Normalizar también las columnas del mrna_dataset para machear con SAMPLE_ID + # Crear un mapeo de columnas normalizadas a columnas originales + normalized_columns = {} + for col in mrna_dataset.columns: + if col != 'Standard_Symbol': + # Extraer la parte base del nombre de la columna + normalized_col = col.rsplit('.', 1)[0] if '.' in col else col + normalized_columns[col] = normalized_col + + # Obtener valores de SAMPLE_ID del DataFrame de metadatos + valid_sample_ids = set(self.clinical_df['SAMPLE_ID']) + valid_columns = [col for col, norm_col in normalized_columns.items() if norm_col in valid_sample_ids] + + # Filtrar el dataset de RNA-Seq para mantener solo las columnas válidas + mrna_dataset = mrna_dataset[['Standard_Symbol'] + valid_columns] + + # Renombrar las columnas para que coincidan con los SAMPLE_ID normalizados + rename_mapping = {col: normalized_columns[col] for col in valid_columns} + mrna_dataset = mrna_dataset.rename(columns=rename_mapping) + + # Eliminar filas con NA en los datos de expresión génica + # Eliminar filas donde Standard_Symbol es NA + mrna_dataset = mrna_dataset.dropna(subset=['Standard_Symbol']) + + # Establecer Standard_Symbol como índice con el nombre Hugo_Symbol y eliminar la columna + mrna_dataset = mrna_dataset.set_index('Standard_Symbol') + mrna_dataset.index.name = 'Hugo_Symbol' + + self.mrna_df = mrna_dataset + logging.info("mRNA data processed successfully.") + + def _transform_to_log2(self) -> None: + """Transform mRNA data to log2 scale. + This method applies a log2 transformation to the mRNA dataset. + """ + + if isinstance(self.mrna_df, pd.DataFrame): + has_negative = (self.mrna_df < 0).any().any() + else: + has_negative = np.any(self.mrna_df < 0) + + if has_negative: + logging.warning("Negative values found in mRNA dataset. Skipping log2 transformation.") + + # Apply log2 transformation + mrna_df_log2 = np.log2(self.mrna_df + 1) + + self.mrna_df = mrna_df_log2 + logging.info("Log2 transformation applied to mRNA data.") + + def _filter_low_expression_genes(self) -> None: + """Filter out low-expression genes from the mRNA dataset. + This method removes genes whose maximum expression across samples is below + a specified percentile threshold. + """ + + # Calculate the average expression for each gene (across all samples) + avg_expression = self.mrna_df.mean(axis=1) + + # Calculate the threshold based on the percentile + threshold = np.percentile(avg_expression, self.threshold_percentile * 100) + + # Filter genes with average expression above or equal to the threshold + genes_to_keep = avg_expression >= threshold + self.mrna_df = self.mrna_df[genes_to_keep] + + self.mrna_df = self.mrna_df + logging.info(f"Filtered low-expression genes. Threshold: {threshold:.2f}") + + + def _filter_genes_by_variance(self) -> None: + """Filter genes based on variance. + This method removes genes whose variance across samples is below + a specified percentile threshold. + """ + + # Calculate variance for each gene (row) + variances = self.mrna_df.var(axis=1) + + # Filter out genes with variance below the threshold + genes_to_keep = variances > self.threshold + self.mrna_df = self.mrna_df[genes_to_keep] + + # Check if we still have features after filtering + if self.mrna_df.empty or self.mrna_df.shape[0] == 0: + raise ValueError("NO_FEATURES_FOUND") + + logging.info(f"Filtered genes by variance. Threshold: {self.threshold:.2f}") + + + def process_datasets(self) -> tuple[pd.DataFrame, pd.DataFrame]: + """Process the clinical and mRNA datasets. + This method orchestrates the processing steps for both datasets. + + Returns: + Tuple containing the processed clinical and mRNA datasets. + """ + + self.validate_datasets() + self._process_clinical_data() + self._process_mrna_data() + self._transform_to_log2() + self._filter_low_expression_genes() + self._filter_genes_by_variance() + + return self.clinical_df, self.mrna_df \ No newline at end of file diff --git a/src/differential_expression/tasks.py b/src/differential_expression/tasks.py new file mode 100644 index 00000000..4f17819e --- /dev/null +++ b/src/differential_expression/tasks.py @@ -0,0 +1,117 @@ +import logging +import time + +from celery.contrib.abortable import AbortableTask +from celery.exceptions import SoftTimeLimitExceeded +from django.conf import settings +from multiomics_intermediate.celery import app + +from common.exceptions import EmptyDataset, ExperimentStopped +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState, +) +from .service import DifferentialExpressionService + +@app.task(bind=True, base=AbortableTask, acks_late=True, reject_on_worker_lost=True, + soft_time_limit=settings.FS_SOFT_TIME_LIMIT) +def eval_differential_expression_experiment(self, experiment_pk: int, ): + """Evaluate differential expression for a given experiment. + + @param self: Self instance of the Celery task (available due to bind=True). + @param experiment_pk: Primary key of the experiment to evaluate. + """ + # Check if the experiment exists + try: + experiment : DifferentialExpressionExperiment = DifferentialExpressionExperiment.objects.get(pk=experiment_pk) + except DifferentialExpressionExperiment.DoesNotExist: + logging.error(f'DifferentialExpressionExperiment {experiment_pk} does not exist') + return + + # Check if the experiment has reached the limit of attempts + if experiment.attempt >= 3: + logging.warning(f'DifferentialExpressionExperiment {experiment.pk} has reached attempts limit.') + experiment.state = DifferentialExpressionExperimentState.REACHED_ATTEMPTS_LIMIT + experiment.save(update_fields=['state']) + return + + # Increment the attempt and set the state of the experiment to IN_PROCESS + experiment.attempt += 1 + experiment.state = DifferentialExpressionExperimentState.IN_PROCESS + experiment.save(update_fields=['attempt', 'state']) + + try: + logging.warning(f'Starting evaluation for DifferentialExpressionExperiment ID -> {experiment.pk}') + + # Compute the differential expression experiment + start = time.time() + + compute_differential_expression = DifferentialExpressionService(experiment, is_aborted=self.is_aborted) + result = compute_differential_expression.perform_differential_expression() + + total_execution_time = time.time() - start + logging.info(f'DifferentialExpressionExperiment {experiment.pk} processed in {total_execution_time:.2f} seconds.') + + # If user cancel the experiment, discard changes + if self.is_aborted(): + experiment.state = DifferentialExpressionExperimentState.STOPPING + experiment.save(update_fields=['state']) + raise ExperimentStopped + + # Save the results to the database if we got results + if result is not None: + experiment.save_results(result) + logging.info(f'Results saved for DifferentialExpressionExperiment {experiment.pk}') + + experiment.execution_time = total_execution_time + experiment.save(update_fields=['execution_time']) + + experiment.state = DifferentialExpressionExperimentState.COMPLETED + experiment.save(update_fields=['state']) + + except EmptyDataset: + logging.error(f'Empty dataset error for DifferentialExpressionExperiment {experiment.pk}') + experiment.state = DifferentialExpressionExperimentState.EMPTY_DATASET + experiment.save(update_fields=['state']) + return + + except SoftTimeLimitExceeded as e: + # If celery soft time limit is exceeded, sets the experiment as TIMEOUT_EXCEEDED + logging.warning(f'DifferentialExpressionExperiment {experiment.pk} has exceeded the soft time limit') + logging.exception(e) + experiment.state = DifferentialExpressionExperimentState.TIMEOUT_EXCEEDED + experiment.save(update_fields=['state']) + return + + except ValueError as e: + error_msg = str(e) + if error_msg == "NO_SAMPLES_IN_COMMON": + logging.error(f'No samples in common for DifferentialExpressionExperiment {experiment.pk}') + experiment.state = DifferentialExpressionExperimentState.NO_SAMPLES_IN_COMMON + experiment.save(update_fields=['state']) + return + elif error_msg == "NO_FEATURES_FOUND": + logging.error(f'No features found after filtering for DifferentialExpressionExperiment {experiment.pk}') + experiment.state = DifferentialExpressionExperimentState.NO_FEATURES_FOUND + experiment.save(update_fields=['state']) + return + else: + # Handle other ValueError cases + logging.error(f'ValueError during evaluation of DifferentialExperimentExperiment {experiment.pk}: {e}') + experiment.state = DifferentialExpressionExperimentState.FINISHED_WITH_ERROR + experiment.save(update_fields=['state']) + raise e + + except Exception as e: + logging.error(f'Error during evaluation of DifferentialExpressionExperiment {experiment.pk}: {e}') + experiment.state = DifferentialExpressionExperimentState.FINISHED_WITH_ERROR + experiment.save(update_fields=['state']) + raise e + finally: + # Ensure that the experiment is marked as finished + if self.is_aborted(): + logging.info(f'Evaluation for DifferentialExpressionExperiment {experiment.pk} was aborted.') + experiment.state = DifferentialExpressionExperimentState.STOPPED + experiment.save(update_fields=['state']) + else: + logging.info(f'Evaluation for DifferentialExpressionExperiment {experiment.pk} completed successfully.') \ No newline at end of file diff --git a/src/differential_expression/tests/__init__.py b/src/differential_expression/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/differential_expression/tests/test_integration.py b/src/differential_expression/tests/test_integration.py new file mode 100644 index 00000000..2f0ca64a --- /dev/null +++ b/src/differential_expression/tests/test_integration.py @@ -0,0 +1,224 @@ +""" +Integration tests for differential expression analysis. +These tests execute the full pipeline including the Celery task and R/limma analysis. +""" +from unittest.mock import patch +from django.test import TestCase, override_settings +from django.contrib.auth.models import User +from common.tests_utils import create_user_file +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState, + DifferentialExpressionExperimentResult +) +from differential_expression.tests.test_utils import ( + get_test_file_path, + create_differential_expression_source, + create_differential_expression_clinical_source, + create_test_differential_expression_experiment +) +from differential_expression.tasks import eval_differential_expression_experiment +from user_files.models_choices import FileType + + +def mock_is_aborted(): + """Mock is_aborted to always return False for testing.""" + return False + + +@override_settings( + CELERY_TASK_ALWAYS_EAGER=True, + CELERY_TASK_EAGER_PROPAGATES=True +) +@patch.object(eval_differential_expression_experiment, 'is_aborted', mock_is_aborted) +class DifferentialExpressionIntegrationTestCase(TestCase): + """ + Integration tests that execute the full differential expression pipeline. + Uses CELERY_TASK_ALWAYS_EAGER to run Celery tasks synchronously. + """ + + def setUp(self): + """Test setup""" + # Create test user + self.user = User.objects.create_user( + username='testuser', + email='test@test.com', + password='testpass123' + ) + + # Create test files with real TCGA data + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.user + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.user + ) + + # Create sources + self.mrna_source = create_differential_expression_source(self.mrna_file) + self.clinical_source = create_differential_expression_clinical_source(self.clinical_file) + + def test_full_differential_expression_analysis_by_sex(self): + """ + Test the complete differential expression analysis pipeline. + Analyzes gene expression differences between Male and Female samples. + """ + # Create experiment with SEX as the clinical attribute + experiment = create_test_differential_expression_experiment( + name='Integration Test - Sex Analysis', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + clinical_attribute='SEX', + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE + ) + + # Verify initial state + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.WAITING_FOR_QUEUE) + self.assertEqual(experiment.attempt, 0) + + # Execute the Celery task synchronously + eval_differential_expression_experiment(experiment.pk) + + # Refresh from database + experiment.refresh_from_db() + + # Verify the experiment completed successfully + self.assertEqual( + experiment.state, + DifferentialExpressionExperimentState.COMPLETED, + f"Experiment should be COMPLETED but is {experiment.get_state_display()}" + ) + self.assertEqual(experiment.attempt, 1) + self.assertGreater(experiment.execution_time, 0) + + # Verify results were saved + results_count = experiment.results.count() + self.assertGreater(results_count, 0, "Should have differential expression results") + + # Verify result structure + first_result = experiment.results.first() + self.assertIsNotNone(first_result.gene) + self.assertIsNotNone(first_result.p_value) + self.assertIsNotNone(first_result.adj_p_val) + self.assertIsNotNone(first_result.log_fc) + + def test_differential_expression_results_dataframe(self): + """ + Test that results can be retrieved as a pandas DataFrame. + """ + # Create and run experiment + experiment = create_test_differential_expression_experiment( + name='Integration Test - DataFrame Results', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + clinical_attribute='SEX', + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE + ) + + # Execute the task + eval_differential_expression_experiment(experiment.pk) + + # Refresh and get results as DataFrame + experiment.refresh_from_db() + results_df = experiment.get_results_dataframe() + + # Verify DataFrame structure + self.assertIsNotNone(results_df) + self.assertGreater(len(results_df), 0) + + # Check expected columns + expected_columns = ['AveExpr', 'P.Value', 'adj.P.Val', 'logFC', 't', 'B'] + for col in expected_columns: + self.assertIn(col, results_df.columns, f"Missing column: {col}") + + # Verify genes are in the index + self.assertTrue(len(results_df.index) > 0, "DataFrame should have genes as index") + + def test_differential_expression_significant_genes(self): + """ + Test filtering for significant differentially expressed genes. + """ + # Create and run experiment + experiment = create_test_differential_expression_experiment( + name='Integration Test - Significant Genes', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + clinical_attribute='SEX', + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE + ) + + # Execute the task + eval_differential_expression_experiment(experiment.pk) + + # Refresh from database + experiment.refresh_from_db() + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.COMPLETED) + + # Test get_significant_genes with relaxed thresholds for test data + significant_genes = experiment.get_significant_genes( + p_value_threshold=1.0, # Relaxed for small test dataset + log_fc_threshold=0.0 + ) + + # Should return some results (we use relaxed thresholds) + self.assertIsNotNone(significant_genes) + + def test_experiment_state_transitions(self): + """ + Test that experiment goes through correct state transitions. + """ + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Integration Test - State Transitions', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + clinical_attribute='SEX', + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE + ) + + # Initial state + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.WAITING_FOR_QUEUE) + + # Execute task + eval_differential_expression_experiment(experiment.pk) + + # Final state should be COMPLETED + experiment.refresh_from_db() + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.COMPLETED) + + # Attempt should be incremented + self.assertEqual(experiment.attempt, 1) + + def test_experiment_execution_time_recorded(self): + """ + Test that execution time is recorded after analysis. + """ + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Integration Test - Execution Time', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + clinical_attribute='SEX', + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE + ) + + # Initial execution time should be 0 + self.assertEqual(experiment.execution_time, 0.0) + + # Execute task + eval_differential_expression_experiment(experiment.pk) + + # Execution time should be recorded + experiment.refresh_from_db() + self.assertGreater(experiment.execution_time, 0) \ No newline at end of file diff --git a/src/differential_expression/tests/test_models.py b/src/differential_expression/tests/test_models.py new file mode 100644 index 00000000..6a312dfe --- /dev/null +++ b/src/differential_expression/tests/test_models.py @@ -0,0 +1,294 @@ +from django.test import TestCase +from django.contrib.auth.models import User +from common.tests_utils import create_user_file +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState, + DifferentialExpressionExperimentResult +) +from differential_expression.tests.test_utils import ( + get_test_file_path, + create_differential_expression_source, + create_differential_expression_clinical_source, + create_test_differential_expression_experiment +) +from user_files.models_choices import FileType +import pandas as pd + + +class DifferentialExpressionExperimentModelTestCase(TestCase): + """Tests for DifferentialExpressionExperiment model""" + + def setUp(self): + """Test setup""" + # Create test user + self.user = User.objects.create_user( + username='testuser', + email='test@test.com', + password='testpass123' + ) + + # Create test files + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.user + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.user + ) + + # Create sources + self.mrna_source = create_differential_expression_source(self.mrna_file) + self.clinical_source = create_differential_expression_clinical_source(self.clinical_file) + + def test_create_experiment(self): + """Test creating a differential expression experiment""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user + ) + + self.assertIsNotNone(experiment.id) + self.assertEqual(experiment.name, 'Test Experiment') + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.WAITING_FOR_QUEUE) + self.assertEqual(experiment.user, self.user) + + def test_experiment_str_representation(self): + """Test string representation of experiment""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user + ) + + self.assertEqual( + str(experiment), + 'Differential Expression Experiment: Test Experiment' + ) + + def test_experiment_default_values(self): + """Test experiment default values""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user + ) + + self.assertEqual(experiment.threshold_percentile, 0.15) + self.assertEqual(experiment.threshold, 0.0001) + self.assertEqual(experiment.top, 100) + self.assertEqual(experiment.attempt, 0) + self.assertEqual(experiment.execution_time, 0.0) + self.assertFalse(experiment.is_public) + + def test_save_and_retrieve_results(self): + """Test saving and retrieving differential expression results""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Create test results dataframe + results_data = { + 'AveExpr': [5.2, 6.1, 7.3], + 'P.Value': [0.001, 0.05, 0.0001], + 'adj.P.Val': [0.01, 0.1, 0.001], + 'logFC': [2.5, -1.8, 3.2], + 't': [4.5, -3.2, 5.1], + 'B': [2.1, 1.5, 3.2] + } + results_df = pd.DataFrame(results_data, index=['GENE_1', 'GENE_2', 'GENE_3']) + + # Save results + experiment.save_results(results_df) + + # Retrieve results + retrieved_df = experiment.get_results_dataframe() + + # Assert results were saved correctly + self.assertIsNotNone(retrieved_df) + self.assertEqual(len(retrieved_df), 3) + self.assertEqual(set(retrieved_df.index), {'GENE_1', 'GENE_2', 'GENE_3'}) + + # Check specific values + self.assertAlmostEqual(retrieved_df.loc['GENE_1', 'AveExpr'], 5.2, places=1) + self.assertAlmostEqual(retrieved_df.loc['GENE_1', 'logFC'], 2.5, places=1) + + def test_get_significant_genes(self): + """Test getting significant genes""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Create test results + DifferentialExpressionExperimentResult.objects.create( + experiment=experiment, + gene='GENE_1', + ave_expr=5.2, + p_value=0.001, + adj_p_val=0.01, + log_fc=2.5, + t_statistic=4.5, + b_statistic=2.1 + ) + DifferentialExpressionExperimentResult.objects.create( + experiment=experiment, + gene='GENE_2', + ave_expr=6.1, + p_value=0.05, + adj_p_val=0.1, + log_fc=-0.5, + t_statistic=-1.2, + b_statistic=1.5 + ) + DifferentialExpressionExperimentResult.objects.create( + experiment=experiment, + gene='GENE_3', + ave_expr=7.3, + p_value=0.0001, + adj_p_val=0.001, + log_fc=3.2, + t_statistic=5.1, + b_statistic=3.2 + ) + + # Get significant genes with default thresholds (p_value <= 0.05, |log_fc| >= 1.0) + significant_genes = experiment.get_significant_genes( + p_value_threshold=0.05, + log_fc_threshold=1.0 + ) + + # Should return GENE_1 and GENE_3 (both have adj_p_val <= 0.05 and |log_fc| >= 1.0) + self.assertEqual(significant_genes.count(), 2) + gene_names = [result.gene for result in significant_genes] + self.assertIn('GENE_1', gene_names) + self.assertIn('GENE_3', gene_names) + + def test_delete_experiment_cascades(self): + """Test that deleting experiment cascades to results""" + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Create test results + DifferentialExpressionExperimentResult.objects.create( + experiment=experiment, + gene='GENE_1', + ave_expr=5.2, + p_value=0.001, + adj_p_val=0.01, + log_fc=2.5, + t_statistic=4.5, + b_statistic=2.1 + ) + + experiment_id = experiment.id + + # Delete experiment + experiment.delete() + + # Assert experiment and results were deleted + self.assertFalse( + DifferentialExpressionExperiment.objects.filter(pk=experiment_id).exists() + ) + self.assertFalse( + DifferentialExpressionExperimentResult.objects.filter( + experiment_id=experiment_id + ).exists() + ) + + +class DifferentialExpressionSourceModelTestCase(TestCase): + """Tests for DifferentialExpressionSource models""" + + def setUp(self): + """Test setup""" + # Create test user + self.user = User.objects.create_user( + username='testuser', + email='test@test.com', + password='testpass123' + ) + + # Create test files + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.user + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.user + ) + + def test_create_mrna_source(self): + """Test creating an mRNA source""" + source = create_differential_expression_source(self.mrna_file) + + self.assertIsNotNone(source.id) + self.assertEqual(source.user_file, self.mrna_file) + + def test_create_clinical_source(self): + """Test creating a clinical source""" + source = create_differential_expression_clinical_source(self.clinical_file) + + self.assertIsNotNone(source.id) + self.assertEqual(source.user_file, self.clinical_file) + + def test_get_samples_from_mrna_source(self): + """Test getting samples from mRNA source""" + source = create_differential_expression_source(self.mrna_file) + + samples = source.get_samples() + + # Based on mrna_test.csv (6 TCGA samples as columns) + self.assertEqual(len(samples), 6) + self.assertIn('TCGA-OR-A5J1-01', samples) + self.assertIn('TCGA-OR-A5J8-01', samples) + + def test_get_samples_from_clinical_source(self): + """Test getting samples from clinical source""" + source = create_differential_expression_clinical_source(self.clinical_file) + + samples = source.get_samples() + + # Based on clinical_test.csv (6 TCGA samples as row indices - PATIENT_ID) + self.assertEqual(len(samples), 6) + self.assertIn('TCGA-OR-A5J1', samples) + self.assertIn('TCGA-OR-A5J8', samples) + + def test_get_clinical_attributes(self): + """Test getting clinical attributes""" + source = create_differential_expression_clinical_source(self.clinical_file) + + attributes = source.get_attributes() + + # Based on clinical_test.csv (OTHER_PATIENT_ID, SEX, OS_STATUS, OS_MONTHS, SAMPLE_ID, OTHER_SAMPLE_ID) + self.assertEqual(len(attributes), 6) + self.assertIn('SEX', attributes) + self.assertIn('OS_STATUS', attributes) + self.assertIn('OS_MONTHS', attributes) + self.assertIn('SAMPLE_ID', attributes) diff --git a/src/differential_expression/tests/test_utils.py b/src/differential_expression/tests/test_utils.py new file mode 100644 index 00000000..dbd59d63 --- /dev/null +++ b/src/differential_expression/tests/test_utils.py @@ -0,0 +1,90 @@ +import os +from typing import Optional +from django.contrib.auth.models import User +from api_service.models import ExperimentSource, ExperimentClinicalSource +from common.tests_utils import create_user_file +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState, + DifferentialExpressionTool +) +from user_files.models import UserFile +from user_files.models_choices import FileType + + +def get_test_file_path(filename: str) -> str: + """ + Gets the absolute file's path in test folder + @param filename: File's name + @return: Absolute path to the file in test folder + """ + dir_name = os.path.dirname(__file__) + file_path = os.path.join(dir_name, f'tests_files/{filename}') + return file_path + + +def create_differential_expression_source(user_file: UserFile) -> ExperimentSource: + """ + Creates a new instance of ExperimentSource saved in DB + @param user_file: UserFile to create the ExperimentSource + @return: ExperimentSource saved instance + """ + source = ExperimentSource.objects.create(user_file=user_file) + return source + + +def create_differential_expression_clinical_source(user_file: UserFile) -> ExperimentClinicalSource: + """ + Creates a new instance of ExperimentClinicalSource saved in DB + @param user_file: UserFile to create the ExperimentClinicalSource + @return: ExperimentClinicalSource saved instance + """ + source = ExperimentClinicalSource.objects.create(user_file=user_file) + return source + + +def create_test_differential_expression_experiment( + name: str, + clinical_source: ExperimentClinicalSource, + mrna_source: ExperimentSource, + user: User, + state: DifferentialExpressionExperimentState = DifferentialExpressionExperimentState.WAITING_FOR_QUEUE, + description: str = 'Test experiment', + clinical_attribute: str = 'SEX', + tool: DifferentialExpressionTool = DifferentialExpressionTool.DESEQ, + threshold_percentile: float = 0.15, + threshold: float = 0.0001, + top: int = 100, + task_id: Optional[str] = None +) -> DifferentialExpressionExperiment: + """ + Create a test DifferentialExpressionExperiment object + @param name: Experiment name + @param clinical_source: Clinical data source + @param mrna_source: mRNA data source + @param user: User who owns the experiment + @param state: Initial experiment state + @param description: Experiment description + @param clinical_attribute: Clinical attribute to use for analysis + @param tool: Tool to use for differential expression analysis + @param threshold_percentile: Threshold percentile for filtering + @param threshold: Threshold for filtering + @param top: Number of top results to keep + @param task_id: Optional Celery task ID + @return: Saved DifferentialExpressionExperiment instance + """ + experiment = DifferentialExpressionExperiment.objects.create( + name=name, + description=description, + clinical_source=clinical_source, + mrna_source=mrna_source, + clinical_attribute=clinical_attribute, + tool=tool, + threshold_percentile=threshold_percentile, + threshold=threshold, + top=top, + state=state, + user=user, + task_id=task_id + ) + return experiment diff --git a/src/differential_expression/tests/test_views.py b/src/differential_expression/tests/test_views.py new file mode 100644 index 00000000..1d7f17ae --- /dev/null +++ b/src/differential_expression/tests/test_views.py @@ -0,0 +1,718 @@ +from django.test import TestCase +from django.contrib.auth.models import User +from rest_framework.test import APIClient +from rest_framework import status +from unittest.mock import patch, MagicMock +from common.tests_utils import create_user_file +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState +) +from differential_expression.tests.test_utils import ( + get_test_file_path, + create_differential_expression_source, + create_differential_expression_clinical_source, + create_test_differential_expression_experiment +) +from user_files.models_choices import FileType + + +class DifferentialExpressionDeleteTestCase(TestCase): + """Tests for DifferentialExpressionDelete endpoint""" + + def setUp(self): + """Test setup""" + # Create test users + self.owner = User.objects.create_user( + username='owner', + email='owner@test.com', + password='testpass123' + ) + self.other_user = User.objects.create_user( + username='other', + email='other@test.com', + password='testpass123' + ) + + # Create test files + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.owner + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.owner + ) + + # Create sources + self.mrna_source = create_differential_expression_source(self.mrna_file) + self.clinical_source = create_differential_expression_clinical_source(self.clinical_file) + + # Create API client + self.client = APIClient() + + def test_delete_experiment_success(self): + """Test successfully deleting a completed experiment""" + # Create a completed experiment + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert response + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + + # Assert experiment was deleted + self.assertFalse( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_not_owner(self): + """Test that non-owner cannot delete experiment""" + # Create experiment owned by owner + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as other user + self.client.force_authenticate(user=self.other_user) + + # Try to delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse(response.data['ok']) + + # Assert experiment still exists + self.assertTrue( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_running_in_process(self): + """Test that running experiment cannot be deleted""" + # Create a running experiment + experiment = create_test_differential_expression_experiment( + name='Running Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.IN_PROCESS, + task_id='test-task-id' + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert bad request + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(response.data['ok']) + self.assertIn('running', response.data['detail'].lower()) + + # Assert experiment still exists + self.assertTrue( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_waiting_for_queue(self): + """Test that experiment waiting for queue cannot be deleted""" + # Create experiment waiting for queue + experiment = create_test_differential_expression_experiment( + name='Waiting Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.WAITING_FOR_QUEUE, + task_id='test-task-id' + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert bad request + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(response.data['ok']) + + # Assert experiment still exists + self.assertTrue( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_stopping(self): + """Test that experiment being stopped cannot be deleted""" + # Create experiment being stopped + experiment = create_test_differential_expression_experiment( + name='Stopping Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.STOPPING, + task_id='test-task-id' + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert bad request + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(response.data['ok']) + + # Assert experiment still exists + self.assertTrue( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_stopped_success(self): + """Test that stopped experiment can be deleted""" + # Create stopped experiment + experiment = create_test_differential_expression_experiment( + name='Stopped Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.STOPPED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert success + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + + # Assert experiment was deleted + self.assertFalse( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_with_error_success(self): + """Test that experiment with error can be deleted""" + # Create experiment with error + experiment = create_test_differential_expression_experiment( + name='Error Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.FINISHED_WITH_ERROR + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert success + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + + # Assert experiment was deleted + self.assertFalse( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_not_authenticated(self): + """Test that unauthenticated user cannot delete experiment""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Test Experiment', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Don't authenticate + + # Try to delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert unauthorized + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + # Assert experiment still exists + self.assertTrue( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + def test_delete_experiment_not_found(self): + """Test deleting non-existent experiment returns 404""" + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to delete non-existent experiment + url = '/differential-expression/delete/99999/' + response = self.client.delete(url) + + # Assert not found + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + @patch('differential_expression.views.AbortableAsyncResult') + def test_delete_experiment_with_active_task(self, mock_async_result): + """Test that experiment with active Celery task gets aborted before deletion""" + # Mock the AbortableAsyncResult + mock_result = MagicMock() + mock_result.state = 'STARTED' + mock_result.abort.return_value = True + mock_async_result.return_value = mock_result + + # Create completed experiment with task_id + experiment = create_test_differential_expression_experiment( + name='Test Experiment with Task', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED, + task_id='active-task-id' + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Delete the experiment + url = f'/differential-expression/delete/{experiment.pk}/' + response = self.client.delete(url) + + # Assert success + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + + # Assert AbortableAsyncResult was called + mock_async_result.assert_called_once_with('active-task-id') + mock_result.abort.assert_called_once() + + # Assert experiment was deleted + self.assertFalse( + DifferentialExpressionExperiment.objects.filter(pk=experiment.pk).exists() + ) + + +class DifferentialExpressionListTestCase(TestCase): + """Tests for DifferentialExpressionList endpoint""" + + def setUp(self): + """Test setup""" + # Create test user + self.user = User.objects.create_user( + username='testuser', + email='test@test.com', + password='testpass123' + ) + + # Create test files + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.user + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.user + ) + + # Create sources + self.mrna_source = create_differential_expression_source(self.mrna_file) + self.clinical_source = create_differential_expression_clinical_source(self.clinical_file) + + # Create API client + self.client = APIClient() + + def test_list_experiments_authenticated(self): + """Test listing experiments when authenticated""" + # Create some experiments + create_test_differential_expression_experiment( + name='Experiment 1', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + state=DifferentialExpressionExperimentState.COMPLETED + ) + create_test_differential_expression_experiment( + name='Experiment 2', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.user, + state=DifferentialExpressionExperimentState.IN_PROCESS + ) + + # Authenticate + self.client.force_authenticate(user=self.user) + + # Get list + response = self.client.get('/differential-expression/') + + # Assert success + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 2) + + def test_list_experiments_not_authenticated(self): + """Test that unauthenticated users cannot list experiments""" + # Don't authenticate + response = self.client.get('/differential-expression/') + + # Assert forbidden (DRF returns 403 for unauthenticated requests) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + +class DifferentialExpressionUpdateTestCase(TestCase): + """Tests for DifferentialExpressionUpdate endpoint""" + + def setUp(self): + """Test setup""" + # Create test users + self.owner = User.objects.create_user( + username='owner', + email='owner@test.com', + password='testpass123' + ) + self.other_user = User.objects.create_user( + username='other', + email='other@test.com', + password='testpass123' + ) + + # Create test files + self.mrna_file = create_user_file( + get_test_file_path('mrna_test.csv'), + 'mRNA Test', + FileType.MRNA, + self.owner + ) + self.clinical_file = create_user_file( + get_test_file_path('clinical_test.csv'), + 'Clinical Test', + FileType.CLINICAL, + self.owner + ) + + # Create sources + self.mrna_source = create_differential_expression_source(self.mrna_file) + self.clinical_source = create_differential_expression_clinical_source(self.clinical_file) + + # Create API client + self.client = APIClient() + + def test_update_experiment_name_and_description_success(self): + """Test successfully updating both name and description""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Update the experiment + url = f'/differential-expression/update/{experiment.pk}/' + data = { + 'name': 'Updated Name', + 'description': 'Updated Description' + } + response = self.client.patch(url, data, format='json') + + # Assert response + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + self.assertEqual(response.data['data']['name'], 'Updated Name') + self.assertEqual(response.data['data']['description'], 'Updated Description') + + # Assert experiment was updated in database + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Updated Name') + self.assertEqual(experiment.description, 'Updated Description') + + def test_update_experiment_name_only_success(self): + """Test successfully updating only the name""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Update only the name + url = f'/differential-expression/update/{experiment.pk}/' + data = {'name': 'New Name Only'} + response = self.client.patch(url, data, format='json') + + # Assert response + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + self.assertEqual(response.data['data']['name'], 'New Name Only') + self.assertEqual(response.data['data']['description'], 'Original Description') + + # Assert experiment was updated in database + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'New Name Only') + self.assertEqual(experiment.description, 'Original Description') + + def test_update_experiment_description_only_success(self): + """Test successfully updating only the description""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Update only the description + url = f'/differential-expression/update/{experiment.pk}/' + data = {'description': 'New Description Only'} + response = self.client.patch(url, data, format='json') + + # Assert response + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + self.assertEqual(response.data['data']['name'], 'Original Name') + self.assertEqual(response.data['data']['description'], 'New Description Only') + + # Assert experiment was updated in database + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + self.assertEqual(experiment.description, 'New Description Only') + + def test_update_experiment_not_owner(self): + """Test that non-owner cannot update experiment""" + # Create experiment owned by owner + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as other user + self.client.force_authenticate(user=self.other_user) + + # Try to update the experiment + url = f'/differential-expression/update/{experiment.pk}/' + data = {'name': 'Hacked Name'} + response = self.client.patch(url, data, format='json') + + # Assert forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse(response.data['ok']) + + # Assert experiment was not updated + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + + def test_update_experiment_no_fields_provided(self): + """Test that update fails when no fields are provided""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to update with no fields + url = f'/differential-expression/update/{experiment.pk}/' + data = {} + response = self.client.patch(url, data, format='json') + + # Assert bad request + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(response.data['ok']) + self.assertIn('at least one field', response.data['detail'].lower()) + + # Assert experiment was not updated + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + self.assertEqual(experiment.description, 'Original Description') + + def test_update_experiment_not_authenticated(self): + """Test that unauthenticated user cannot update experiment""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Don't authenticate + + # Try to update the experiment + url = f'/differential-expression/update/{experiment.pk}/' + data = {'name': 'Hacked Name'} + response = self.client.patch(url, data, format='json') + + # Assert unauthorized + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + # Assert experiment was not updated + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + + def test_update_experiment_not_found(self): + """Test updating non-existent experiment returns 404""" + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to update non-existent experiment + url = '/differential-expression/update/99999/' + data = {'name': 'New Name'} + response = self.client.patch(url, data, format='json') + + # Assert not found + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_experiment_empty_name(self): + """Test that updating experiment with empty name fails""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Try to update with empty name + url = f'/differential-expression/update/{experiment.pk}/' + data = {'name': ''} + response = self.client.patch(url, data, format='json') + + # Assert bad request + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertFalse(response.data['ok']) + self.assertIn('cannot be empty', response.data['detail'].lower()) + + # Assert experiment was not updated + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + + def test_update_experiment_empty_description(self): + """Test that updating experiment with empty description succeeds""" + # Create experiment + experiment = create_test_differential_expression_experiment( + name='Original Name', + description='Original Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.COMPLETED + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Update with empty description (should be allowed) + url = f'/differential-expression/update/{experiment.pk}/' + data = {'description': ''} + response = self.client.patch(url, data, format='json') + + # Assert success (empty description is valid) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + self.assertEqual(response.data['data']['name'], 'Original Name') + self.assertEqual(response.data['data']['description'], '') + + # Assert experiment was updated in database + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Original Name') + self.assertEqual(experiment.description, '') + + def test_update_running_experiment(self): + """Test that running experiment can be updated (only name/description, not execution)""" + # Create a running experiment + experiment = create_test_differential_expression_experiment( + name='Running Experiment', + description='Running Description', + clinical_source=self.clinical_source, + mrna_source=self.mrna_source, + user=self.owner, + state=DifferentialExpressionExperimentState.IN_PROCESS, + task_id='test-task-id' + ) + + # Authenticate as owner + self.client.force_authenticate(user=self.owner) + + # Update the experiment + url = f'/differential-expression/update/{experiment.pk}/' + data = {'name': 'Updated Name While Running'} + response = self.client.patch(url, data, format='json') + + # Assert success (name/description can be updated even while running) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(response.data['ok']) + self.assertEqual(response.data['data']['name'], 'Updated Name While Running') + + # Assert experiment was updated in database but state is still IN_PROCESS + experiment.refresh_from_db() + self.assertEqual(experiment.name, 'Updated Name While Running') + self.assertEqual(experiment.state, DifferentialExpressionExperimentState.IN_PROCESS) diff --git a/src/differential_expression/tests/tests_files/clinical_test.csv b/src/differential_expression/tests/tests_files/clinical_test.csv new file mode 100644 index 00000000..87d9867a --- /dev/null +++ b/src/differential_expression/tests/tests_files/clinical_test.csv @@ -0,0 +1,7 @@ +PATIENT_ID,OTHER_PATIENT_ID,SEX,OS_STATUS,OS_MONTHS,SAMPLE_ID,OTHER_SAMPLE_ID +TCGA-OR-A5J1,B3164F7B-C826-4E08-9EE6-8FF96D29B913,Male,1:DECEASED,44.51,TCGA-OR-A5J1-01,E4038EBB-6E6D-44B1-84AD-E35AAFCA7B70 +TCGA-OR-A5J2,8E7C2E31-D085-4B75-A970-162526DD07A0,Female,1:DECEASED,55.09,TCGA-OR-A5J2-01,46B7EB7C-E5F7-476D-A68C-5972F947445F +TCGA-OR-A5J3,DFD687BC-6E69-42F7-AF94-D17FC150D1A1,Female,0:LIVING,68.69,TCGA-OR-A5J3-01,1FB59B6F-53C0-4B14-82CC-77CD55C67AD6 +TCGA-OR-A5J5,802DBD0D-EF07-4C91-AB8D-1DD39532E947,Male,1:DECEASED,11.99,TCGA-OR-A5J5-01,C6214F9B-35C8-424C-B6FE-BEC1A9317C0A +TCGA-OR-A5J6,C8898B42-B704-45A0-9829-144B98F416E0,Female,0:LIVING,88.8,TCGA-OR-A5J6-01,C0C9DD1B-98B1-4A20-8927-3C0B75A4559E +TCGA-OR-A5J8,08E0D412-D4D8-4D13-B792-A4DD0BD9EC2B,Male,1:DECEASED,19.02,TCGA-OR-A5J8-01,2F47F06E-8F76-440E-BBCE-B7C90635A59E diff --git a/src/differential_expression/tests/tests_files/mrna_test.csv b/src/differential_expression/tests/tests_files/mrna_test.csv new file mode 100644 index 00000000..ac8c756e --- /dev/null +++ b/src/differential_expression/tests/tests_files/mrna_test.csv @@ -0,0 +1,11 @@ +Standard_Symbol,TCGA-OR-A5J1-01,TCGA-OR-A5J2-01,TCGA-OR-A5J3-01,TCGA-OR-A5J5-01,TCGA-OR-A5J6-01,TCGA-OR-A5J8-01 +LOC100130426,0.0,0.0,0.0,0.0,0.0,0.0 +UBE2Q2P3,3.2661,2.6815,1.7301,0.0,0.0,1.4422 +HMGB1P1,149.1354,81.0777,86.4879,53.9117,66.9063,94.9316 +TIMM23,2034.1018,1304.9251,1054.6586,2350.8908,1257.9864,995.0269 +MOXD2,0.0,0.0,0.0,0.0,0.0,0.0 +LOC155060,274.2555,199.302,348.3928,439.1944,149.2147,377.9528 +RNU12-2P,1.4409,0.0,0.5925,0.7746,0.0,1.6577 +EZHIP,17.7714,0.4026,0.5925,0.7746,0.0,1.2433 +EFCAB8,0.0,0.4026,0.5925,0.7746,2.7943,0.8288 +SRP14P1,11.5274,5.2342,7.7026,6.1967,10.6183,5.3875 diff --git a/src/differential_expression/urls.py b/src/differential_expression/urls.py new file mode 100644 index 00000000..19c38e54 --- /dev/null +++ b/src/differential_expression/urls.py @@ -0,0 +1,21 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.DifferentialExpressionList.as_view(), name='differential_expression_list'), + path('results/', views.DifferentialExpressionDetail.as_view(), name='differential_expression_results'), + path('results//', views.DifferentialExpressionResults.as_view()), + path('volcano-data', views.DifferentialExpressionVolcanoData.as_view(), name='differential_expression_volcano_data'), + path('volcano-data//', views.DifferentialExpressionVolcanoData.as_view()), + path('submit-experiment', views.DifferentialExpressionSubmit.as_view(), name='differential_expression_submit'), + path('stop-experiment', views.DifferentialExpressionStop.as_view(), name='differential_expression_stop'), + path('update', views.DifferentialExpressionUpdate.as_view(), name='differential_expression_update'), + path('update//', views.DifferentialExpressionUpdate.as_view()), + path('delete', views.DifferentialExpressionDelete.as_view(), name='differential_expression_delete'), + path('delete//', views.DifferentialExpressionDelete.as_view()), + path('download-results', views.download_differential_expression_results, name='download_differential_expression_results'), + path('download-results//', views.download_differential_expression_results), + path('get-common-samples-differential-experiment', views.GetCommonSamplesDifferentialExperiment.as_view(), name='get_common_samples_differential_experiment'), + path('get-common-samples-one-front-differential-experiment', views.GetCommonSamplesDifferentialOneFrontExperiment.as_view(), name='get_common_samples_one_front_differential_experiment'), + path('switch-institution-public-view', views.ToggleDiffExperimentPublicView.as_view(), name='switch-diff-experiment-public-view'), + ] \ No newline at end of file diff --git a/src/differential_expression/views.py b/src/differential_expression/views.py new file mode 100644 index 00000000..92004208 --- /dev/null +++ b/src/differential_expression/views.py @@ -0,0 +1,846 @@ +import logging +from typing import Optional, Dict, Tuple, List + +import numpy as np +import pandas as pd +from celery.contrib.abortable import AbortableAsyncResult +from django.contrib.auth.decorators import login_required +from django.core.files.base import ContentFile +from django.db import transaction +from django.db.models import Q +from django.http import HttpResponse +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import filters, generics, permissions, status +from rest_framework.exceptions import ValidationError +from rest_framework.generics import get_object_or_404 +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + +from api_service.enums import SourceType, CommonSamplesStatusErrorCode +from api_service.utils import get_cgds_dataset +from common.enums import ResponseCode +from common.functions import get_enum_from_value, get_intersection, encode_json_response_status +from common.pagination import StandardResultsSetPagination +from common.response import ResponseStatus +from api_service.models import ExperimentClinicalSource, ExperimentSource +from datasets_synchronization.models import CGDSDataset +from datasets_synchronization.models import CGDSStudy +from differential_expression.models import ( + DifferentialExpressionExperiment, + DifferentialExpressionExperimentState, +) +from differential_expression.serializers import ( + DifferentialExpressionExperimentDetailSerializer, + DifferentialExpressionExperimentResultSerializer, + DifferentialExpressionExperimentListSerializer, + DifferentialExpressionVolcanoPlotSerializer, +) +from tissues.models import Tissue +from user_files.models import UserFile +from user_files.models_choices import FileType +from user_files.views import get_an_user_file +from .tasks import eval_differential_expression_experiment + + +def create_differential_expression_source( + source_type: int, + request: Request, + file_type: FileType, + prefix: str +) -> tuple[ + ExperimentSource | ExperimentClinicalSource | None, ExperimentClinicalSource | None +]: + """ + Creates a Source object for differential expression experiments. + """ + is_clinical = prefix == 'clinical' + source = ExperimentClinicalSource() if is_clinical else ExperimentSource() + clinical_source = None + + if source_type == SourceType.NEW_DATASET.value: + # Adds a new User's file and uses it + source_file = getattr(request, 'FILES', {}).get(f'{prefix}File') + if source_file is None: + return None, None + + user_file = UserFile( + name=source_file.name, + description=None, + file_obj=source_file, + file_type=file_type, + user=request.user + ) + user_file.save() + user_file.compute_post_saved_field() + source.user_file = user_file + + elif source_type == SourceType.CGDS.value: + # Gets the CGDS Study + post_data = getattr(request, 'data', {}) or getattr(request, 'POST', {}) + cgds_study_pk = post_data.get(f'{prefix}CGDSStudyPk') + if not cgds_study_pk: + return None, None + + cgds_study = CGDSStudy.objects.get(pk=int(cgds_study_pk)) + + if is_clinical: + # For clinical sources, we need both datasets + if cgds_study.clinical_patient_dataset and cgds_study.clinical_sample_dataset: + source.cgds_dataset = cgds_study.clinical_patient_dataset + source.extra_cgds_dataset = cgds_study.clinical_sample_dataset + else: + return None, None + else: + cgds_dataset = get_cgds_dataset(cgds_study, file_type) + if cgds_dataset: + source.cgds_dataset = cgds_dataset + else: + return None, None + + else: + # Uses an existing User's file + post_data = getattr(request, 'data', {}) or getattr(request, 'POST', {}) + existing_file_pk = post_data.get(f'{prefix}ExistingFilePk') + if not existing_file_pk: + return None, None + + user_file = get_an_user_file(user=request.user, user_file_pk=int(existing_file_pk)) + source.user_file = user_file + + source.save() + return source, clinical_source + + +class DifferentialExpressionDetail(generics.RetrieveAPIView): + """ + Endpoint to retrieve a differential expression experiment. + """ + + def get_queryset(self) -> DifferentialExpressionExperiment: + """ + Retrieve the differential expression experiment by its ID. + """ + user = self.request.user + experiment_id = self.kwargs.get('pk') + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=experiment_id) + # Check if the user has access to the experiment + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + raise ValidationError('You do not have permission to access this experiment.') + + return experiment.results.all() + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] + search_fields = ['gene'] + serializer_class = DifferentialExpressionExperimentDetailSerializer + permission_classes = [permissions.IsAuthenticated] + + +class DifferentialExpressionList(generics.ListAPIView): + """ + Endpoint to list all differential expression experiments. + Returns only the fields: id, Name, Description, Date, State, Sources and if it is public. + """ + + def get_queryset(self): + """ + Endpoint to list all differential expression experiments. + """ + user = self.request.user + experiments = DifferentialExpressionExperiment.objects.filter( + Q(is_public=True) | + Q(user=user) | + Q(shared_institutions__institutionadministration__user=user) | + Q(shared_users=user) + ).distinct() + + return experiments + + serializer_class = DifferentialExpressionExperimentListSerializer + permission_classes = [permissions.IsAuthenticated] + pagination_class = StandardResultsSetPagination + filter_backends = [filters.OrderingFilter, filters.SearchFilter, DjangoFilterBackend] + search_fields = ['name', 'description'] + + +class DifferentialExpressionRetrieve(generics.RetrieveAPIView): + """ + Endpoint to retrieve a single differential expression experiment by ID. + """ + serializer_class = DifferentialExpressionExperimentListSerializer + permission_classes = [permissions.IsAuthenticated] + + def get_queryset(self): + """ + Returns experiments the user has access to. + """ + user = self.request.user + return DifferentialExpressionExperiment.objects.filter( + Q(is_public=True) | + Q(user=user) | + Q(shared_institutions__institutionadministration__user=user) | + Q(shared_users=user) + ).distinct() + + def get_object(self): + """ + Retrieves the experiment and checks permissions. + """ + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=self.kwargs.get('pk')) + user = self.request.user + + # Check if the user has access to the experiment + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + raise ValidationError('You do not have permission to access this experiment.') + + return experiment + filterset_fields = ['tool'] + + +class DifferentialExpressionSubmit(APIView): + """ + Endpoint to submit a differential expression experiment. + """ + + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def post(request: Request): + """ + Endpoint to submit a differential expression experiment. + """ + with transaction.atomic(): + # Get basic experiment data + post_data = getattr(request, 'data', {}) or getattr(request, 'POST', {}) + + name = post_data.get('name', 'Differential Expression Experiment') + description = post_data.get('description', '') + + # Clinical source + clinical_source_type = post_data.get('clinicalType') + + if clinical_source_type: + clinical_source_type = int(clinical_source_type) + clinical_source, clinical_aux = create_differential_expression_source( + clinical_source_type, request, FileType.CLINICAL, 'clinical') + # Select the valid one (if it's a CGDSStudy it needs clinical_aux as it has both needed CGDSDatasets) + clinical_source = clinical_aux if clinical_aux is not None else clinical_source + else: + clinical_source = None + + if clinical_source is None: + raise ValidationError('Invalid clinical source') + + # mRNA source + mrna_source_type = post_data.get('mRNAType') + if mrna_source_type: + mrna_source_type = int(mrna_source_type) + mrna_source, _mrna_clinical = create_differential_expression_source( + mrna_source_type, request, FileType.MRNA, 'mRNA') + else: + mrna_source = None + + if mrna_source is None: + raise ValidationError('Invalid mRNA source') + + # Clinical attribute + clinical_attribute = post_data.get('clinicalAttribute') + if not clinical_attribute: + raise ValidationError('Clinical attribute is required') + + # Threshold percentile + try: + threshold_percentile_str = post_data.get('thresholdPercentile', '0.15') + threshold_percentile = float(threshold_percentile_str) + except (ValueError, TypeError) as exc: + raise ValidationError('Invalid threshold percentile value') from exc + + if threshold_percentile < 0 or threshold_percentile > 1: + raise ValidationError('Threshold percentile must be between 0 and 1') + + # Threshold + try: + threshold_str = post_data.get('threshold', '0.0001') + threshold = float(threshold_str) + except (ValueError, TypeError) as exc: + raise ValidationError('Invalid threshold value') from exc + + if threshold < 0 or threshold > 1: + raise ValidationError('Threshold must be between 0 and 1') + + # Top parameter + try: + top_str = post_data.get('top', '100') + top = int(top_str) + except (ValueError, TypeError) as exc: + raise ValidationError('Invalid top value') from exc + + if top < 1 or top > 1000: + raise ValidationError('Top must be between 1 and 1000') + + # Create the differential expression experiment + experiment = DifferentialExpressionExperiment.objects.create( + name=name, + description=description, + clinical_source=clinical_source, + mrna_source=mrna_source, + clinical_attribute=clinical_attribute, + threshold_percentile=threshold_percentile, + threshold=threshold, + top=top, + user=request.user, + ) + + # Start the async task + async_res = eval_differential_expression_experiment.apply_async( + (experiment.pk,), queue='differential_expression') + + experiment.task_id = async_res.task_id + experiment.save(update_fields=['task_id']) + + return Response({'ok': True}) + + +class DifferentialExpressionResults(generics.ListAPIView): + """ + Endpoint to get results for a differential expression experiment. + Supports filtering by p-value and fold-change thresholds via query parameters. + """ + serializer_class = DifferentialExpressionExperimentResultSerializer + permission_classes = [permissions.IsAuthenticated] + pagination_class = StandardResultsSetPagination + filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend] + search_fields = ['gene'] + ordering_fields = ['adj_p_val', 'log_fc', 'p_value', 'ave_expr'] + ordering = ['adj_p_val'] # Default ordering by adjusted p-value + + def get_queryset(self): + experiment_id = self.kwargs.get('pk') + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=experiment_id) + + # Check permissions + user = self.request.user + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + raise ValidationError('You do not have permission to access this experiment.') + + # Get query parameters for filtering (optional) + p_threshold = self.request.query_params.get('p_threshold') + fc_threshold = self.request.query_params.get('fc_threshold') + + # If filtering parameters are provided, apply filtering + if p_threshold is not None or fc_threshold is not None: + try: + p_threshold = float(p_threshold) if p_threshold is not None else 0.05 + fc_threshold = float(fc_threshold) if fc_threshold is not None else 2.0 + except ValueError as exc: + raise ValidationError('Invalid threshold values. Must be numeric.') from exc + + # Validate thresholds + if p_threshold < 0 or p_threshold > 1: + raise ValidationError('p_threshold must be between 0 and 1') + if fc_threshold < 0: + raise ValidationError('fc_threshold must be positive') + + return experiment.get_significant_genes(p_threshold, fc_threshold) + + # Return all results if no filtering parameters + return experiment.results.all() + + +class DifferentialExpressionVolcanoData(generics.ListAPIView): + """ + Endpoint to get all results for a volcano plot visualization. + Returns all results without pagination in a format suitable for volcano plots. + Format: [{id, label, log2FC, pValue}, ...] + """ + serializer_class = DifferentialExpressionVolcanoPlotSerializer + permission_classes = [permissions.IsAuthenticated] + pagination_class = None # Disable pagination for volcano plot + + def get_queryset(self): + """Get all results for the specified experiment with permission checks.""" + pk = self.kwargs.get('pk') + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=pk) + + # Check permissions + user = self.request.user + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + raise ValidationError('You do not have permission to access this experiment.') + + # Return all results (no pagination) + return experiment.results.all() + + +class DifferentialExpressionStop(APIView): + """ + Endpoint to stop a differential expression experiment. + """ + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def get(request: Request): + experiment_id = request.GET.get('experimentId') + if not experiment_id: + raise ValidationError('experimentId is required.') + + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=experiment_id) + + user = request.user + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + raise ValidationError('You do not have permission to access this experiment.') + + # Task state and validation + if not experiment.task_id: + return Response({'ok': False, 'detail': 'The experiment does not have an associated task.'}) + + # If it's no running, there's nothing to stop + if experiment.state in [ + DifferentialExpressionExperimentState.COMPLETED, + DifferentialExpressionExperimentState.STOPPED, + DifferentialExpressionExperimentState.FINISHED_WITH_ERROR, + DifferentialExpressionExperimentState.TIMEOUT_EXCEEDED, + DifferentialExpressionExperimentState.REACHED_ATTEMPTS_LIMIT, + DifferentialExpressionExperimentState.EMPTY_DATASET, + DifferentialExpressionExperimentState.NO_SAMPLES_IN_COMMON, + DifferentialExpressionExperimentState.NO_FEATURES_FOUND, + ]: + return Response({'ok': False, 'detail': f'The experiment is not running. Status: {experiment.state}'}) + + # Try to abort the AbortableTask + try: + async_res = AbortableAsyncResult(experiment.task_id) + aborted = async_res.abort() # Signals the task to abort (self.is_aborted() == True) + except Exception as e: + return Response({'ok': False, 'detail': f'The task could not be stopped: {e}'}) + + # Mark the experiment as STOPPING; the task will mark it as STOPPED in the finally block + experiment.state = DifferentialExpressionExperimentState.STOPPING + experiment.save(update_fields=['state']) + + return Response({'ok': bool(aborted)}) + + +class GetCommonSamplesDifferentialExperiment(APIView): + """Gets the number of in common samples between two datasets""" + + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def get(request: Request): + mrna_source_id = request.GET.get('mRNASourceId') + mrna_source_type = request.GET.get('mRNASourceType') + clinical_source_id = request.GET.get('clinicalSourceId') + clinical_source_type = request.GET.get('clinicalSourceType') + if None in [mrna_source_id, mrna_source_type, clinical_source_id, + clinical_source_type]: + response = { + 'status': ResponseStatus( + ResponseCode.ERROR, + message='Invalid request params', + internal_code=CommonSamplesStatusErrorCode.INVALID_PARAMS + ), + } + else: + # Cast parameters + mrna_source_id = int(mrna_source_id) + mrna_source_type = get_enum_from_value( + int(mrna_source_type), SourceType) + + clinical_source_id = int(clinical_source_id) + clinical_source_type = get_enum_from_value(int(clinical_source_type), SourceType) + + # Gets df + samples_list_mrna, response = get_samples_list( + mrna_source_id, + mrna_source_type, + FileType.MRNA, + request.user + ) + + # Response will be != None if an error occurred + if response is None: + samples_list_clinical, response = get_samples_list( + clinical_source_id, + clinical_source_type, + FileType.CLINICAL, + request.user + ) + intersection = get_intersection(samples_list_mrna, samples_list_clinical) + # Gets intersection + + if response is None: + response = { + 'status': ResponseStatus(ResponseCode.SUCCESS), + 'data': { + 'number_samples_mrna': len(samples_list_mrna) if samples_list_mrna is not None else 0, + 'number_samples_clinical': len( + samples_list_clinical) if samples_list_clinical is not None else 0, + 'number_samples_in_common': intersection.size + } + } + + # Formats to JSON the ResponseStatus object + return encode_json_response_status(response) + + +class GetCommonSamplesDifferentialOneFrontExperiment(APIView): + permission_classes = [permissions.IsAuthenticated] + """Gets the number of in common samples between two datasets, one in the backend and other in the frontend""" + + @staticmethod + def post(request: Request): + post_data = getattr(request, 'data', {}) or getattr(request, 'POST', {}) + headers_in_front: Optional[List[str]] = post_data.get('headersColumnsNames') + other_source_id = post_data.get('otherSourceId') + other_source_type = post_data.get('otherSourceType') + other_source_file_type = post_data.get('otherSourceFileType') + + if headers_in_front is None or other_source_id is None or other_source_type is None or other_source_file_type is None: + response = { + 'status': ResponseStatus( + ResponseCode.ERROR, + message='Invalid request params', + internal_code=CommonSamplesStatusErrorCode.INVALID_PARAMS + ), + } + else: + # Cast parameters + other_source_id = int(other_source_id) + other_source_type = get_enum_from_value( + int(other_source_type), SourceType) + + # Gets df + samples_list_1, response = get_samples_list( + other_source_id, + other_source_type, + other_source_file_type, + request.user + ) + + # Response will be != None if an error occurred + if response is None: + intersection: np.ndarray = get_intersection( + samples_list_1, headers_in_front) + response = { + 'status': ResponseStatus(ResponseCode.SUCCESS), + 'data': { + 'number_samples_backend': len(samples_list_1) if samples_list_1 else 0, + 'number_samples_in_common': intersection.size + } + } + + # Formats to JSON the ResponseStatus object + return encode_json_response_status(response) + + +class ToggleDiffExperimentPublicView(APIView): + """ + API endpoint to toggle the 'is_public' field of an experiment. + Only the owner of the experiment can perform this action. + """ + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def post(request): + """ + Toggle the 'is_public' field of the experiment. + """ + data = request.data + experiment_id = data.get('experimentId') + experiment = get_object_or_404(DifferentialExpressionExperiment, id=experiment_id) + if experiment.user.id != request.user.id: + return Response( + {"error": "You do not have permission to modify this experiment."}, + status=status.HTTP_403_FORBIDDEN + ) + + experiment.is_public = not experiment.is_public + experiment.save(update_fields=['is_public']) + + return Response( + {"id": experiment.id, "is_public": experiment.is_public} + ) + + +class DifferentialExpressionUpdate(APIView): + """ + Endpoint to update name and description of a differential expression experiment. + Only the owner can update the experiment. + """ + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def patch(request: Request, pk: int): + """ + Update name, description and/or tissues of a differential expression experiment. + """ + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=pk) + + # Only the owner can update the experiment + if experiment.user.id != request.user.id: + return Response( + {'ok': False, 'detail': 'You do not have permission to update this experiment.'}, + status=403 + ) + + # Get the data from request + data = request.data + name = data.get('name') + description = data.get('description') + tissue_id = data.get('tissue') + + # Validate that at least one field is provided + if name is None and description is None and tissue_id is None: + return Response( + {'ok': False, 'detail': 'At least one field (name, description or tissue) must be provided.'}, + status=400 + ) + + # Validate that name is not empty if provided + if name is not None and not name: + return Response( + {'ok': False, 'detail': 'Experiment name cannot be empty.'}, + status=400 + ) + + # Update fields if provided + fields_to_update = [] + if name is not None: + experiment.name = name + fields_to_update.append('name') + if description is not None: + experiment.description = description + fields_to_update.append('description') + if tissue_id is not None: + tissue = get_object_or_404(Tissue, pk=tissue_id) + experiment.tissue = tissue + fields_to_update.append('tissue') + + # Save the experiment + experiment.save(update_fields=fields_to_update) + + return Response({ + 'ok': True, + 'data': { + 'id': experiment.id, + 'name': experiment.name, + 'description': experiment.description, + 'tissue': {'id': experiment.tissue.id, 'name': experiment.tissue.name} if experiment.tissue else None + } + }) + + +class DifferentialExpressionDelete(APIView): + """ + Endpoint to delete a differential expression experiment. + Only the owner can delete the experiment. + The experiment must not be running (must be completed, stopped, or in an error state). + """ + permission_classes = [permissions.IsAuthenticated] + + @staticmethod + def delete(request: Request, pk: int): + """ + Delete a differential expression experiment. + """ + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=pk) + + # Only the owner can delete the experiment + if experiment.user.id != request.user.id: + return Response( + {'ok': False, 'detail': 'You do not have permission to delete this experiment.'}, + status=403 + ) + + # Check if the experiment is currently running + running_states = [ + DifferentialExpressionExperimentState.IN_PROCESS, + DifferentialExpressionExperimentState.WAITING_FOR_QUEUE, + DifferentialExpressionExperimentState.STOPPING, + ] + + if experiment.state in running_states: + return Response( + { + 'ok': False, + 'detail': f'Cannot delete experiment while it is running. Current state: {experiment.get_state_display()}. Please stop the experiment first.' + }, + status=400 + ) + + # If the experiment has a task_id and is somehow still active, abort it + if experiment.task_id: + try: + async_res = AbortableAsyncResult(experiment.task_id) + if async_res.state in ['PENDING', 'STARTED', 'RETRY']: + async_res.abort() + except Exception as ex: + # If we can't abort, continue with deletion anyway since state shows it's not running + logging.exception(ex) + + # Delete the experiment (cascade will delete related objects) + experiment.delete() + + return Response({'ok': True}) + + +def get_samples_list( + id_source: int, + type_source: Optional[SourceType], + file_type: Optional[FileType], + user +) -> Tuple[Optional[List[str]], Optional[Dict]]: + """ + Gets a DataFrame from the file retrieve from DB or MongoDB with an id and SourceType. + @param id_source: ID of the UserFile/CGDSDataset to retrieve. + @param type_source: Source type to check if it's a UserFile or a CGDSDataset. + @param file_type: FileType (mRNA, miRNA, etc.) to get the corresponding CGDSDataset. + @param user: Current logged user to retrieve only his datasets. + @return: A DataFrame (if corresponds) and a Response dict (the dataset doesn't exist). + """ + list_of_samples = None + response = None + if type_source is None: + response = { + 'status': ResponseStatus( + ResponseCode.ERROR, + message=f'The source type {type_source} does not exist', + internal_code=CommonSamplesStatusErrorCode.SOURCE_TYPE_DOES_NOT_EXISTS + ), + } + elif type_source == SourceType.UPLOADED_DATASETS: + try: + user_file = get_an_user_file(user=user, user_file_pk=id_source) + if file_type == FileType.CLINICAL: + list_of_samples = user_file.get_first_column_of_all_rows() + else: + list_of_samples = user_file.get_column_names() + except UserFile.DoesNotExist: + response = { + 'status': ResponseStatus( + ResponseCode.ERROR, + message=f'The UserFile with id = {id_source} does not exist', + internal_code=CommonSamplesStatusErrorCode.DATASET_DOES_NOT_EXISTS + ), + } + elif type_source == SourceType.CGDS: + try: + # Gets the CGDS Study + cgds_study = CGDSStudy.objects.get(pk=id_source) + + # Gets the corresponding Study's Dataset + cgds_dataset = get_cgds_dataset(cgds_study, file_type) + + list_of_samples = cgds_dataset.get_column_names() + + except CGDSDataset.DoesNotExist: + response = { + 'status': ResponseStatus( + ResponseCode.ERROR, + message=f'The CGDS dataset with id = {id_source} does not exist', + internal_code=CommonSamplesStatusErrorCode.DATASET_DOES_NOT_EXISTS + ), + } + + return list_of_samples, response + + +@login_required +def download_differential_expression_results(request, pk: int): + """ + Downloads all the differential expression results for a specific experiment. + Returns a TSV file with all results (no pagination). + Supports optional filtering via query parameters: + - adj_p_val: adjusted p-value threshold (returns genes with adj_p_val <= adj_p_val) + - log_fc: log fold change threshold (returns genes with |log_fc| >= log_fc) + """ + experiment = get_object_or_404(DifferentialExpressionExperiment, pk=pk) + + # Check permissions + user = request.user + if not (experiment.is_public or + experiment.user == user or + experiment.shared_institutions.filter(institutionadministration__user=user).exists() or + experiment.shared_users.filter(id=user.id).exists()): + return HttpResponse('Unauthorized', status=401) + + # Get filter parameters from query string + adj_p_val = request.GET.get('adj_p_val') + log_fc = request.GET.get('log_fc') + + results = experiment.results.all() + filename_suffix = '' + + # Apply adj_p_val filter if provided + if adj_p_val is not None: + try: + adj_p_val = float(adj_p_val) + except ValueError: + return HttpResponse('Invalid adj_p_val. Must be numeric.', status=400) + + if adj_p_val < 0 or adj_p_val > 1: + return HttpResponse('adj_p_val must be between 0 and 1', status=400) + + results = results.filter(adj_p_val__lte=adj_p_val) + filename_suffix += f'_p{adj_p_val}' + + # Apply log_fc filter if provided + if log_fc is not None: + try: + log_fc = float(log_fc) + except ValueError: + return HttpResponse('Invalid log_fc. Must be numeric.', status=400) + + if log_fc < 0: + return HttpResponse('log_fc must be >= 0', status=400) + + results = results.filter( + Q(log_fc__gte=log_fc) | Q(log_fc__lte=-log_fc) + ) + filename_suffix += f'_fc{log_fc}' + + if not filename_suffix: + filename_suffix = '_all' + + results = results.order_by('adj_p_val') + + if not results.exists(): + return HttpResponse('No results found for this experiment', status=404) + + # Convert results to list of dictionaries + results_data = [] + for result in results: + results_data.append({ + 'gene': result.gene, + 'log_fc': result.log_fc, + 'ave_expr': result.ave_expr, + 't_statistic': result.t_statistic, + 'p_value': result.p_value, + 'adj_p_val': result.adj_p_val, + 'b_statistic': result.b_statistic + }) + + # Create DataFrame and convert to TSV + df = pd.DataFrame(results_data) + file_to_send = ContentFile(df.to_csv(sep='\t', decimal='.', index=False)) + + # Generate HTTP response for file download + response = HttpResponse(file_to_send, 'text/csv') + response['Content-Length'] = file_to_send.size + response['Content-Disposition'] = f'attachment; filename="{experiment.name}_results{filename_suffix}.tsv"' + + return response diff --git a/src/django-runserver.err.log b/src/django-runserver.err.log new file mode 100644 index 00000000..4b731d26 --- /dev/null +++ b/src/django-runserver.err.log @@ -0,0 +1,1309 @@ +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET / 200 [0.06, 127.0.0.1:53898] +HTTP GET / 200 [0.01, 127.0.0.1:57472] +HTTP GET /static/frontend/dist/base-5e4454e0d573701c.css 200 [0.03, 127.0.0.1:57472] +HTTP GET /static/frontend/dist/main-8fcbf5b36d15958d.css 200 [0.05, 127.0.0.1:60584] +HTTP GET /static/frontend/dist/base-698434567de94a4f.js 200 [0.07, 127.0.0.1:60623] +HTTP GET /static/frontend/dist/main-66653c008c9d7cc3.js 200 [0.34, 127.0.0.1:52618] +HTTP GET /static/frontend/img/logo.png 200 [0.00, 127.0.0.1:52618] +HTTP GET /static/frontend/img/homepage/multiomix-logo-name.png 200 [0.01, 127.0.0.1:60623] +HTTP GET /static/frontend/img/homepage/all-analysis.png 200 [0.01, 127.0.0.1:57472] +HTTP GET /static/frontend/dist/a90d9f6676840d19.woff2 200 [0.01, 127.0.0.1:57472] +Forbidden: /users/user +HTTP GET /users/user 403 [0.01, 127.0.0.1:53782] +HTTP GET /static/frontend/dist/620019ed9d1100b6.woff2 200 [0.03, 127.0.0.1:60623] +HTTP GET /static/frontend/dist/3c1ce4656e3163e6.woff2 200 [0.03, 127.0.0.1:52618] +HTTP GET /static/frontend/dist/7edea186e9687169.woff2 200 [0.05, 127.0.0.1:60584] +HTTP GET /static/%20frontend/img/favicon.ico 404 [0.12, 127.0.0.1:60584] +HTTP GET /users/login 200 [0.05, 127.0.0.1:60584] +HTTP GET /static/frontend/dist/login-f776254ef24c0b68.css 200 [0.02, 127.0.0.1:60584] +HTTP GET /static/frontend/dist/login-64d6bcc8c8880b82.js 200 [0.24, 127.0.0.1:52618] +HTTP GET /static/frontend/img/logo-login.png 200 [0.01, 127.0.0.1:52618] +HTTP GET /static/frontend/dist/df34cb4e5bfc4912.jpg 200 [0.02, 127.0.0.1:60584] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP POST /users/authenticate 200 [0.32, 127.0.0.1:60584] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP POST /users/authenticate 302 [0.40, 127.0.0.1:60584] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET / 200 [0.03, 127.0.0.1:60584] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.06, 127.0.0.1:60584] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.05, 127.0.0.1:60584] +HTTP GET /static/frontend/dist/gem-1e83898efe303076.css 200 [0.01, 127.0.0.1:60584] +HTTP GET /static/frontend/dist/gem-27584e3e9b16c6ce.js 200 [0.06, 127.0.0.1:52618] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:55317] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:55317] +HTTP GET /static/frontend/img/profiles/mRNA.svg 200 [0.02, 127.0.0.1:52618] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:61468] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /static/frontend/img/profiles/miRNA.svg 200 [0.04, 127.0.0.1:60584] +HTTP GET /users/user 200 [0.06, 127.0.0.1:57472] +HTTP GET /tags/?type=2 200 [0.06, 127.0.0.1:60623] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:61468] +HTTP GET /api-service/last-experiments 200 [0.08, 127.0.0.1:52618] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:49524] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:49524] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [0.77, 127.0.0.1:65335] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [0.77, 127.0.0.1:53782] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.04, 127.0.0.1:53782] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:55317] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:49524] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:61468] +HTTP GET /static/frontend/dist/files-212c6b9cb00ba2aa.css 200 [0.01, 127.0.0.1:53782] +HTTP GET /static/frontend/dist/files-4aac97f0e0936f3e.js 200 [0.38, 127.0.0.1:65335] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:60611] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:60611] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.10, 127.0.0.1:65335] +HTTP GET /tissues/ 200 [0.10, 127.0.0.1:52618] +HTTP GET /users/user 200 [0.11, 127.0.0.1:57472] +HTTP GET /institutions/my-institutions 200 [0.11, 127.0.0.1:53782] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.20, 127.0.0.1:60623] +C:\github\multiomix\src\user_files\views.py changed, reloading. +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Listen failure: Couldn't listen on 127.0.0.1:8000: [WinError 10048] Solo se permite un uso de cada direccin de socket (protocolo/direccin de red/puerto). +C:\github\multiomix\src\user_files\serializers.py changed, reloading. +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Listen failure: Couldn't listen on 127.0.0.1:8000: [WinError 10048] Solo se permite un uso de cada direccin de socket (protocolo/direccin de red/puerto). +C:\github\multiomix\src\api_service\serializers.py changed, reloading. +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Listen failure: Couldn't listen on 127.0.0.1:8000: [WinError 10048] Solo se permite un uso de cada direccin de socket (protocolo/direccin de red/puerto). +C:\github\multiomix\src\biomarkers\serializers.py changed, reloading. +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:55863] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:55863] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:62075] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:62075] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:60706] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:60706] +C:\github\multiomix\src\datasets_synchronization\serializers.py changed, reloading. +Watching for file changes with StatReloader +"sh" no se reconoce como un comando interno o externo, +programa o archivo por lotes ejecutable. +Error importing in API mode: ImportError('On Windows, cffi mode "ANY" is only "ABI".') +Trying to import in ABI mode. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:51926] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:51926] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:55578] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:55578] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64212] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64212] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.09, 127.0.0.1:57471] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:55578] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:51926] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64212] +HTTP GET /static/frontend/dist/gem-2e9b831389f68878.css 200 [0.00, 127.0.0.1:57471] +HTTP GET /static/frontend/dist/gem-6453e7666b95f994.js 200 [0.02, 127.0.0.1:53850] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:61502] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:61502] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64780] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64780] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:54701] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:54701] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.05, 127.0.0.1:57471] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=2 200 [0.07, 127.0.0.1:53850] +HTTP GET /api-service/last-experiments 200 [0.08, 127.0.0.1:57433] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [0.80, 127.0.0.1:49782] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [0.80, 127.0.0.1:64748] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.04, 127.0.0.1:64748] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:61502] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64780] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:54701] +HTTP GET /static/frontend/dist/files-7dbb733dc5be1cb9.css 200 [0.00, 127.0.0.1:64748] +HTTP GET /static/frontend/dist/files-78dfbf1ed8bc094e.js 200 [0.01, 127.0.0.1:49782] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:54867] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:54867] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.07, 127.0.0.1:49782] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.07, 127.0.0.1:57433] +HTTP GET /users/user 200 [0.07, 127.0.0.1:53850] +HTTP GET /institutions/my-institutions 200 [0.09, 127.0.0.1:64748] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.20, 127.0.0.1:57471] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP PATCH /user-files/6/ 200 [0.05, 127.0.0.1:57471] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.10, 127.0.0.1:57471] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.03, 127.0.0.1:57471] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:54867] +HTTP GET /static/frontend/dist/gem-2e9b831389f68878.css 304 [0.00, 127.0.0.1:57471] +HTTP GET /static/frontend/dist/gem-6453e7666b95f994.js 304 [0.00, 127.0.0.1:64748] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:51918] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:51918] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:62104] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:62104] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:58723] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.09, 127.0.0.1:57471] +HTTP GET /tags/?type=2 200 [0.10, 127.0.0.1:64748] +WebSocket CONNECT /ws/users/1/ [127.0.0.1:58723] +HTTP GET /api-service/last-experiments 200 [0.10, 127.0.0.1:49782] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [0.74, 127.0.0.1:53850] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [0.77, 127.0.0.1:57433] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.03, 127.0.0.1:57433] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:51918] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:62104] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:58723] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 200 [0.00, 127.0.0.1:57433] +HTTP GET /static/frontend/dist/cgds-3b79b879fc1349ef.js 200 [0.01, 127.0.0.1:53850] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:64181] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:64181] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.03, 127.0.0.1:53850] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.07, 127.0.0.1:57433] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.15, 127.0.0.1:49782] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Bad Request: /cgds/studies/3/ +HTTP PATCH /cgds/studies/3/ 400 [0.03, 127.0.0.1:49782] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.08, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.03, 127.0.0.1:63334] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:64181] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 200 [0.00, 127.0.0.1:63334] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 200 [0.02, 127.0.0.1:52296] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:63316] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:63316] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.04, 127.0.0.1:52296] +HTTP GET /users/user 200 [0.06, 127.0.0.1:63334] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.11, 127.0.0.1:64982] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP PATCH /cgds/studies/3/ 200 [0.08, 127.0.0.1:64982] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.09, 127.0.0.1:64982] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.03, 127.0.0.1:64982] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:63316] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 304 [0.00, 127.0.0.1:64982] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 304 [0.00, 127.0.0.1:63334] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:59744] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:59744] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.09, 127.0.0.1:63334] +HTTP GET /users/user 200 [0.09, 127.0.0.1:64982] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.15, 127.0.0.1:52296] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.05, 127.0.0.1:52296] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:59744] +HTTP GET /static/frontend/dist/files-7dbb733dc5be1cb9.css 200 [0.00, 127.0.0.1:52296] +HTTP GET /static/frontend/dist/files-c5f24c7f110f594d.js 200 [0.01, 127.0.0.1:64982] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:58476] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:58476] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.13, 127.0.0.1:64982] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.13, 127.0.0.1:63334] +HTTP GET /users/user 200 [0.17, 127.0.0.1:49723] +HTTP GET /institutions/my-institutions 200 [0.18, 127.0.0.1:52296] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.28, 127.0.0.1:58596] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /biomarkers/ 200 [0.04, 127.0.0.1:58596] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:58476] +HTTP GET /static/frontend/dist/biomarkers-f5e2732ee0388dbd.css 200 [0.00, 127.0.0.1:58596] +HTTP GET /static/frontend/dist/biomarkers-86b367037a6e0eb9.js 200 [0.02, 127.0.0.1:52296] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:50060] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:50060] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.05, 127.0.0.1:49723] +HTTP GET /tags/?type=1 200 [0.07, 127.0.0.1:52296] +HTTP GET /biomarkers/api?search=&page_size=10&page=1&ordering=-upload_date 200 [0.20, 127.0.0.1:58596] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.03, 127.0.0.1:58596] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:50060] +HTTP GET /static/frontend/dist/gem-2e9b831389f68878.css 200 [0.00, 127.0.0.1:58596] +HTTP GET /static/frontend/dist/gem-6453e7666b95f994.js 200 [0.02, 127.0.0.1:52296] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:54339] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:54339] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:56511] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:56511] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:52068] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:52068] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.08, 127.0.0.1:58596] +HTTP GET /tags/?type=2 200 [0.09, 127.0.0.1:52296] +HTTP GET /api-service/last-experiments 200 [0.10, 127.0.0.1:63334] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [1.05, 127.0.0.1:49723] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [1.08, 127.0.0.1:64982] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /biomarkers/ 200 [0.04, 127.0.0.1:64982] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:54339] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:52068] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:56511] +HTTP GET /static/frontend/dist/biomarkers-f5e2732ee0388dbd.css 304 [0.00, 127.0.0.1:64982] +HTTP GET /static/frontend/dist/biomarkers-86b367037a6e0eb9.js 304 [0.00, 127.0.0.1:49723] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64111] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64111] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.07, 127.0.0.1:49723] +HTTP GET /users/user 200 [0.07, 127.0.0.1:64982] +HTTP GET /biomarkers/api?search=&page_size=10&page=1&ordering=-upload_date 200 [0.20, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.04, 127.0.0.1:63334] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64111] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 200 [0.00, 127.0.0.1:63334] +HTTP GET /static/frontend/dist/differentialExpression-5f4dbf1febd8466d.js 200 [0.01, 127.0.0.1:64982] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:49188] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:49188] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.06, 127.0.0.1:64982] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [0.73, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.03, 127.0.0.1:63334] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:49188] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 304 [0.00, 127.0.0.1:63334] +HTTP GET /static/frontend/dist/differentialExpression-5f4dbf1febd8466d.js 304 [0.00, 127.0.0.1:64982] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:58603] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:58603] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.03, 127.0.0.1:64982] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [0.65, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.04, 127.0.0.1:63334] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:58603] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 304 [0.00, 127.0.0.1:63334] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 304 [0.00, 127.0.0.1:64982] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:55278] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:55278] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.07, 127.0.0.1:63334] +HTTP GET /tissues/ 200 [0.07, 127.0.0.1:64982] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.12, 127.0.0.1:49723] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.03, 127.0.0.1:49723] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:55157] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:55157] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.03, 127.0.0.1:49723] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [0.62, 127.0.0.1:64982] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:55278] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.03, 127.0.0.1:64982] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:55157] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 304 [0.00, 127.0.0.1:64982] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 304 [0.00, 127.0.0.1:49723] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:57045] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:57045] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.06, 127.0.0.1:49723] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.08, 127.0.0.1:64982] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.12, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.03, 127.0.0.1:63334] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:57045] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 304 [0.00, 127.0.0.1:63334] +HTTP GET /static/frontend/dist/differentialExpression-5f4dbf1febd8466d.js 304 [0.00, 127.0.0.1:64982] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:51420] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:51420] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.11, 127.0.0.1:64982] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [0.74, 127.0.0.1:63334] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.04, 127.0.0.1:49440] +HTTP GET /static/frontend/dist/base-5e4454e0d573701c.css 200 [0.01, 127.0.0.1:49440] +HTTP GET /static/frontend/dist/files-7dbb733dc5be1cb9.css 304 [0.01, 127.0.0.1:50862] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:51420] +HTTP GET /static/frontend/dist/base-698434567de94a4f.js 200 [0.01, 127.0.0.1:55096] +HTTP GET /static/frontend/dist/files-c5f24c7f110f594d.js 200 [0.02, 127.0.0.1:62622] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:57049] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:57049] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /static/frontend/dist/a90d9f6676840d19.woff2 200 [0.03, 127.0.0.1:64958] +HTTP GET /static/frontend/dist/620019ed9d1100b6.woff2 200 [0.01, 127.0.0.1:64958] +HTTP GET /tags/?type=1 200 [0.09, 127.0.0.1:62622] +HTTP GET /tissues/ 200 [0.09, 127.0.0.1:55096] +HTTP GET /users/user 200 [0.09, 127.0.0.1:50862] +HTTP GET /static/frontend/dist/3c1ce4656e3163e6.woff2 200 [0.01, 127.0.0.1:64958] +HTTP GET /institutions/my-institutions 200 [0.10, 127.0.0.1:49440] +HTTP GET /static/frontend/dist/7edea186e9687169.woff2 200 [0.01, 127.0.0.1:62622] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.16, 127.0.0.1:60712] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.03, 127.0.0.1:60712] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:57049] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 304 [0.00, 127.0.0.1:60712] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 304 [0.00, 127.0.0.1:62622] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:60128] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:60128] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.06, 127.0.0.1:62622] +HTTP GET /users/user 200 [0.07, 127.0.0.1:60712] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.12, 127.0.0.1:49440] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.04, 127.0.0.1:50779] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:60128] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:58373] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:58373] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.08, 127.0.0.1:50779] +HTTP GET /tissues/ 200 [0.08, 127.0.0.1:54641] +HTTP GET /users/user 200 [0.08, 127.0.0.1:65527] +HTTP GET /institutions/my-institutions 200 [0.09, 127.0.0.1:53301] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.18, 127.0.0.1:52637] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP PATCH /user-files/5/ 200 [0.06, 127.0.0.1:52637] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.17, 127.0.0.1:52637] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.05, 127.0.0.1:52637] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:58373] +HTTP GET /static/frontend/dist/gem-2e9b831389f68878.css 304 [0.00, 127.0.0.1:52637] +HTTP GET /static/frontend/dist/gem-6453e7666b95f994.js 304 [0.00, 127.0.0.1:53301] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:53035] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:53035] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64878] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64878] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:55694] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:55694] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=2 200 [0.15, 127.0.0.1:53301] +HTTP GET /users/user 200 [0.18, 127.0.0.1:52637] +HTTP GET /api-service/last-experiments 200 [0.22, 127.0.0.1:50779] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [1.23, 127.0.0.1:65527] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [1.23, 127.0.0.1:54641] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.04, 127.0.0.1:54641] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:53035] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64878] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:55694] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64235] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64235] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.09, 127.0.0.1:54641] +HTTP GET /tissues/ 200 [0.10, 127.0.0.1:50779] +HTTP GET /users/user 200 [0.13, 127.0.0.1:52637] +HTTP GET /institutions/my-institutions 200 [0.14, 127.0.0.1:65527] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.20, 127.0.0.1:53301] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.04, 127.0.0.1:53301] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64235] +HTTP GET /static/frontend/dist/base-5e4454e0d573701c.css 304 [0.00, 127.0.0.1:53301] +HTTP GET /static/frontend/dist/base-698434567de94a4f.js 304 [0.00, 127.0.0.1:65527] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:55581] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:55581] +HTTP GET /static/frontend/dist/a90d9f6676840d19.woff2 304 [0.00, 127.0.0.1:50779] +HTTP GET /static/frontend/dist/7edea186e9687169.woff2 304 [0.01, 127.0.0.1:54641] +HTTP GET /static/frontend/dist/3c1ce4656e3163e6.woff2 304 [0.02, 127.0.0.1:55523] +HTTP GET /static/frontend/dist/620019ed9d1100b6.woff2 304 [0.01, 127.0.0.1:50779] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.10, 127.0.0.1:53301] +HTTP GET /tissues/ 200 [0.10, 127.0.0.1:65527] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.17, 127.0.0.1:52637] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /biomarkers/ 200 [0.04, 127.0.0.1:52637] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:55581] +HTTP GET /static/frontend/dist/biomarkers-f5e2732ee0388dbd.css 304 [0.00, 127.0.0.1:52637] +HTTP GET /static/frontend/dist/biomarkers-86b367037a6e0eb9.js 304 [0.00, 127.0.0.1:65527] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:63750] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:63750] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.21, 127.0.0.1:52637] +HTTP GET /tags/?type=1 200 [0.26, 127.0.0.1:65527] +HTTP GET /biomarkers/api?search=&page_size=10&page=1&ordering=-upload_date 200 [0.70, 127.0.0.1:53301] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.04, 127.0.0.1:53301] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:63750] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 304 [0.00, 127.0.0.1:53301] +HTTP GET /static/frontend/dist/differentialExpression-5f4dbf1febd8466d.js 304 [0.00, 127.0.0.1:65527] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:63626] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:63626] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.07, 127.0.0.1:65527] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [1.09, 127.0.0.1:53301] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /gem 200 [0.06, 127.0.0.1:53301] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:63626] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:61842] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:61842] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:49515] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:49515] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:61019] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:61019] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=2 200 [0.07, 127.0.0.1:53301] +HTTP GET /users/user 200 [0.08, 127.0.0.1:64747] +HTTP GET /api-service/last-experiments 200 [0.14, 127.0.0.1:60545] +HTTP GET /api-service/experiments?page=1&page_size=10&search=&ordering=-submit_date 200 [1.23, 127.0.0.1:61273] +HTTP GET /api-service/experiments?search=&page_size=10&page=1&ordering=-submit_date 200 [1.24, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.04, 127.0.0.1:54525] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:61842] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:61019] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:49515] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:61773] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:61773] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.07, 127.0.0.1:54525] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [0.82, 127.0.0.1:61273] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /institutions/my-institutions 200 [0.04, 127.0.0.1:61273] +HTTP GET /user-files/?file_type=1&with_survival_only=false&search=&page_size=10&page=1&ordering=&visibility=all 200 [0.06, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/studies?file_type=1&search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.06, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /static/frontend/dist/e2b3a9dcfb1fca6e.woff2 200 [0.01, 127.0.0.1:61273] +HTTP GET /assistant/api/conversations/ 200 [0.05, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /assistant/api/conversations/ 200 [0.04, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /assistant/api/conversations/ 200 [0.15, 127.0.0.1:54525] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Internal Server Error: /assistant/api/chat/ +Traceback (most recent call last): + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 489, in thread_handler + raise exc_info[1] + File "C:\github\multiomix\src\venv\Lib\site-packages\django\core\handlers\exception.py", line 42, in inner + response = await get_response(request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 489, in thread_handler + raise exc_info[1] + File "C:\github\multiomix\src\venv\Lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async + response = await wrapped_callback( + ^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 439, in __call__ + ret = await asyncio.shield(exec_coro) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\juani\AppData\Local\Programs\Python\Python312\Lib\concurrent\futures\thread.py", line 58, in run + result = self.fn(*self.args, **self.kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 493, in thread_handler + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\django\views\decorators\csrf.py", line 56, in wrapper_view + return view_func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\django\views\generic\base.py", line 104, in view + return self.dispatch(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 509, in dispatch + response = self.handle_exception(exc) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 469, in handle_exception + self.raise_uncaught_exception(exc) + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception + raise exc + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 506, in dispatch + response = handler(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\assistant\views.py", line 17, in post + from .services.llm_service import run_chat + File "C:\github\multiomix\src\assistant\services\llm_service.py", line 11, in + from langchain.agents import create_agent + File "C:\github\multiomix\src\venv\Lib\site-packages\langchain\agents\__init__.py", line 3, in + from langchain.agents.factory import create_agent + File "C:\github\multiomix\src\venv\Lib\site-packages\langchain\agents\factory.py", line 24, in + from langgraph.graph.state import StateGraph + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\graph\__init__.py", line 2, in + from langgraph.graph.message import MessageGraph, MessagesState, add_messages + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\graph\message.py", line 26, in + from langgraph.graph.state import StateGraph + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\graph\state.py", line 67, in + from langgraph.graph._branch import BranchSpec + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\graph\_branch.py", line 32, in + from langgraph.pregel._write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\pregel\__init__.py", line 1, in + from langgraph.pregel.main import NodeBuilder, Pregel + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\pregel\main.py", line 123, in + from langgraph.pregel._algo import ( + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\pregel\_algo.py", line 77, in + from langgraph.runtime import DEFAULT_RUNTIME, ExecutionInfo, Runtime + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph\runtime.py", line 8, in + from langgraph_sdk.auth.types import BaseUser + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\__init__.py", line 2, in + from langgraph_sdk.client import get_client, get_sync_client + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\client.py", line 12, in + from langgraph_sdk._async.assistants import AssistantsClient + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\_async\__init__.py", line 4, in + from langgraph_sdk._async.client import LangGraphClient, get_client + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\_async\client.py", line 17, in + from langgraph_sdk._async.threads import ThreadsClient + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\_async\threads.py", line 10, in + from langgraph_sdk._async.stream import AsyncThreadStream + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\_async\stream.py", line 28, in + from langgraph_sdk.stream.controller import _SeenEventIds + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\stream\controller.py", line 26, in + from langgraph_sdk.stream.transport import AsyncProtocolTransport, EventStreamHandle + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\stream\transport\__init__.py", line 13, in + from langgraph_sdk.stream.transport.sync_ws import SyncProtocolWebSocketTransport + File "C:\github\multiomix\src\venv\Lib\site-packages\langgraph_sdk\stream\transport\sync_ws.py", line 12, in + from websockets.sync.client import connect as websocket_connect +ModuleNotFoundError: No module named 'websockets.sync' +HTTP POST /assistant/api/chat/ 500 [31.72, 127.0.0.1:54525] +HTTP GET /static/frontend/dist/base-5e4454e0d573701c.css 304 [0.00, 127.0.0.1:57480] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 304 [0.01, 127.0.0.1:63893] +Not Found: /.well-known/appspecific/com.chrome.devtools.json +HTTP GET /.well-known/appspecific/com.chrome.devtools.json 404 [0.01, 127.0.0.1:63893] +HTTP GET /static/frontend/dist/type-guards.js.map 404 [0.05, 127.0.0.1:63893] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.04, 127.0.0.1:63893] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:61773] +HTTP GET /static/frontend/dist/base-698434567de94a4f.js 304 [0.01, 127.0.0.1:63893] +HTTP GET /static/frontend/dist/differentialExpression-5f4dbf1febd8466d.js 304 [0.01, 127.0.0.1:57480] +Not Found: /.well-known/appspecific/com.chrome.devtools.json +HTTP GET /.well-known/appspecific/com.chrome.devtools.json 404 [0.02, 127.0.0.1:57480] +HTTP GET /static/frontend/dist/type-guards.js.map 404 [0.02, 127.0.0.1:57480] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:64549] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:64549] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /static/frontend/dist/a90d9f6676840d19.woff2 304 [0.03, 127.0.0.1:62823] +HTTP GET /static/frontend/dist/7edea186e9687169.woff2 304 [0.03, 127.0.0.1:60535] +HTTP GET /users/user 200 [0.08, 127.0.0.1:57480] +HTTP GET /static/frontend/dist/3c1ce4656e3163e6.woff2 304 [0.03, 127.0.0.1:54913] +HTTP GET /static/frontend/dist/620019ed9d1100b6.woff2 304 [0.03, 127.0.0.1:52279] +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [1.58, 127.0.0.1:63893] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /assistant/api/conversations/ 200 [0.07, 127.0.0.1:63893] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +Xet Storage is enabled for this repo, but the 'hf_xet' package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub[hf_xet]` or `pip install hf_xet` +Internal Server Error: /assistant/api/chat/ +Traceback (most recent call last): + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 489, in thread_handler + raise exc_info[1] + File "C:\github\multiomix\src\venv\Lib\site-packages\django\core\handlers\exception.py", line 42, in inner + response = await get_response(request) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 489, in thread_handler + raise exc_info[1] + File "C:\github\multiomix\src\venv\Lib\site-packages\django\core\handlers\base.py", line 253, in _get_response_async + response = await wrapped_callback( + ^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 439, in __call__ + ret = await asyncio.shield(exec_coro) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\juani\AppData\Local\Programs\Python\Python312\Lib\concurrent\futures\thread.py", line 58, in run + result = self.fn(*self.args, **self.kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\asgiref\sync.py", line 493, in thread_handler + return func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\django\views\decorators\csrf.py", line 56, in wrapper_view + return view_func(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\django\views\generic\base.py", line 104, in view + return self.dispatch(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 509, in dispatch + response = self.handle_exception(exc) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 469, in handle_exception + self.raise_uncaught_exception(exc) + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception + raise exc + File "C:\github\multiomix\src\venv\Lib\site-packages\rest_framework\views.py", line 506, in dispatch + response = handler(request, *args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\assistant\views.py", line 30, in post + reply = run_chat(conversation, message, request.user.pk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\assistant\services\llm_service.py", line 177, in run_chat + reply = asyncio.run(_async_agent(user_message, chat_history, user_id, mcp_config)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\juani\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 194, in run + return runner.run(main) + ^^^^^^^^^^^^^^^^ + File "C:\Users\juani\AppData\Local\Programs\Python\Python312\Lib\asyncio\runners.py", line 118, in run + return self._loop.run_until_complete(task) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\juani\AppData\Local\Programs\Python\Python312\Lib\asyncio\base_events.py", line 664, in run_until_complete + return future.result() + ^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\assistant\services\llm_service.py", line 127, in _async_agent + llm = get_llm() + ^^^^^^^^^ + File "C:\github\multiomix\src\assistant\services\llm_service.py", line 65, in get_llm + return ChatOpenAI( + ^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\langchain_core\load\serializable.py", line 118, in __init__ + super().__init__(*args, **kwargs) + File "C:\github\multiomix\src\venv\Lib\site-packages\pydantic\main.py", line 263, in __init__ + validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\langchain_openai\chat_models\base.py", line 1247, in validate_environment + self.root_client = openai.OpenAI(**client_params, **sync_specific) # type: ignore[arg-type] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\github\multiomix\src\venv\Lib\site-packages\openai\_client.py", line 194, in __init__ + raise OpenAIError( +openai.OpenAIError: Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable. +HTTP POST /assistant/api/chat/ 500 [15.52, 127.0.0.1:63893] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.04, 127.0.0.1:50845] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:64549] +HTTP GET /static/frontend/dist/cgds-5c5ed1de0ce44a6d.js 304 [0.00, 127.0.0.1:53429] +HTTP GET /static/frontend/dist/cgds-7dbb733dc5be1cb9.css 304 [0.01, 127.0.0.1:50845] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:51804] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:51804] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.09, 127.0.0.1:50845] +HTTP GET /users/user 200 [0.09, 127.0.0.1:53429] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.14, 127.0.0.1:53161] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression 200 [0.06, 127.0.0.1:53161] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:51804] +HTTP GET /static/frontend/dist/differentialExpression-c5378a77cc3b1888.css 304 [0.00, 127.0.0.1:53161] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:56905] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:56905] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /users/user 200 [0.05, 127.0.0.1:53161] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /differential-expression/?search=&page_size=10&page=1&ordering=-created_at 200 [1.20, 127.0.0.1:53429] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /institutions/my-institutions 200 [0.05, 127.0.0.1:53429] +HTTP GET /user-files/?file_type=1&with_survival_only=false&search=&page_size=10&page=1&ordering=&visibility=all 200 [0.06, 127.0.0.1:53161] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/studies?file_type=1&search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.05, 127.0.0.1:53161] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /cgds/ 200 [0.04, 127.0.0.1:53161] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:56905] +HTTP GET /static/frontend/dist/base-698434567de94a4f.js 304 [0.00, 127.0.0.1:53161] +WebSocket HANDSHAKING /ws/admins/ [127.0.0.1:63791] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/admins/ [127.0.0.1:63791] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tissues/ 200 [0.06, 127.0.0.1:53161] +HTTP GET /users/user 200 [0.06, 127.0.0.1:53429] +HTTP GET /cgds/studies?search=&page_size=10&page=1&ordering=&only_last_version=true 200 [0.12, 127.0.0.1:50845] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /my-datasets 200 [0.05, 127.0.0.1:50845] +HTTP GET /static/frontend/dist/files-7dbb733dc5be1cb9.css 304 [0.01, 127.0.0.1:50845] +HTTP GET /static/frontend/dist/files-c5f24c7f110f594d.js 304 [0.01, 127.0.0.1:53429] +WebSocket DISCONNECT /ws/admins/ [127.0.0.1:63791] +WebSocket HANDSHAKING /ws/users/1/ [127.0.0.1:53562] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WebSocket CONNECT /ws/users/1/ [127.0.0.1:53562] +HTTP GET /static/frontend/dist/a90d9f6676840d19.woff2 304 [0.00, 127.0.0.1:53385] +HTTP GET /static/frontend/dist/620019ed9d1100b6.woff2 304 [0.01, 127.0.0.1:53385] +HTTP GET /static/frontend/dist/3c1ce4656e3163e6.woff2 304 [0.00, 127.0.0.1:53385] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /static/frontend/dist/7edea186e9687169.woff2 304 [0.00, 127.0.0.1:53385] +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +WARNING: database "multiomics" has a collation version mismatch +DETAIL: The database was created using collation version 2.41, but the operating system provides version 2.36. +HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE multiomics REFRESH COLLATION VERSION, or build PostgreSQL with the right library version. +HTTP GET /tags/?type=1 200 [0.08, 127.0.0.1:53429] +HTTP GET /tissues/ 200 [0.08, 127.0.0.1:53161] +HTTP GET /users/user 200 [0.08, 127.0.0.1:65167] +HTTP GET /institutions/my-institutions 200 [0.10, 127.0.0.1:50845] +HTTP GET /user-files/?search=&page_size=10&page=1&ordering=&visibility=all 200 [0.19, 127.0.0.1:49471] +WebSocket DISCONNECT /ws/users/1/ [127.0.0.1:53562] diff --git a/src/django-runserver.out.log b/src/django-runserver.out.log new file mode 100644 index 00000000..836dc2f3 --- /dev/null +++ b/src/django-runserver.out.log @@ -0,0 +1,42 @@ +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:08:09 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:15:46 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:38:44 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:38:54 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:39:12 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +Performing system checks... + +System check identified no issues (0 silenced). +junio 26, 2026 - 11:40:47 +Django version 4.2.19, using settings 'multiomics_intermediate.settings' +Starting ASGI/Channels version 3.0.5 development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. diff --git a/src/feature_selection/tasks.py b/src/feature_selection/tasks.py index f2e8edd8..9da116da 100644 --- a/src/feature_selection/tasks.py +++ b/src/feature_selection/tasks.py @@ -62,7 +62,7 @@ def eval_feature_selection_experiment(self, experiment_pk: int, fit_fun_enum: Fi start = time.time() molecules_temp_file_path, clinical_temp_file_path, running_in_spark = prepare_and_compute_fs_experiment( experiment, fit_fun_enum, fitness_function_parameters, algorithm_parameters, - cross_validation_parameters, self.is_aborted + cross_validation_parameters, is_aborted=self.is_aborted ) total_execution_time = time.time() - start logging.warning(f'FSExperiment {experiment.pk} total time -> {total_execution_time} seconds') diff --git a/src/frontend/static/frontend/.vscode/settings.json b/src/frontend/static/frontend/.vscode/settings.json new file mode 100644 index 00000000..87f3f165 --- /dev/null +++ b/src/frontend/static/frontend/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "cSpell.words": [ + "'t", + "apexcharts", + "backoff", + "BBHA", + "BENJAMINI", + "BONFERRONI", + "borderless", + "Breusch", + "coeff", + "csrfmiddlewaretoken", + "Cytoscape", + "gamestdio", + "Goldfeld", + "heteroscedasticity", + "HOCHBERG", + "homoscedasticity", + "METHYLATION", + "methylations", + "mirbase", + "MIRNA", + "mirnaxgene", + "modulector", + "MRNA", + "Mult", + "Multiomix", + "n", + "n\\'t", + "ORing", + "pubmed", + "pubmeds", + "Quandt", + "quantile", + "RNAs", + "Serie", + "stackable", + "TCGA", + "visx", + "WIDTHSNUMBER", + "xaxis", + "yaxis", + "YEKUTIELI" + ], + "js/ts.tsdk.path": "node_modules\\typescript\\lib" +} \ No newline at end of file diff --git a/src/frontend/static/frontend/babel.config.js b/src/frontend/static/frontend/babel.config.js new file mode 100644 index 00000000..b8b92b0b --- /dev/null +++ b/src/frontend/static/frontend/babel.config.js @@ -0,0 +1,15 @@ +/** + TODO: This configuration uses Babel and the React Compiler beta for React 18. +Once Ant Design 6 is released and the project is upgraded to React 19, +it is recommended to migrate from Babel to SWC for better performance and official support. + */ +export default { + presets: [ + ['@babel/preset-env', { targets: 'defaults' }], + ['@babel/preset-react', { runtime: 'automatic' }], + '@babel/preset-typescript' + ], + plugins: [ + ['babel-plugin-react-compiler', { target: '18' }] + ] +} \ No newline at end of file diff --git a/src/frontend/static/frontend/css.d.ts b/src/frontend/static/frontend/css.d.ts new file mode 100644 index 00000000..39d076dd --- /dev/null +++ b/src/frontend/static/frontend/css.d.ts @@ -0,0 +1 @@ +declare module '*.css' diff --git a/src/frontend/static/frontend/eslint.config.mjs b/src/frontend/static/frontend/eslint.config.mjs index 2f58e8eb..cffcd606 100644 --- a/src/frontend/static/frontend/eslint.config.mjs +++ b/src/frontend/static/frontend/eslint.config.mjs @@ -8,6 +8,7 @@ import jsdocPlugin from 'eslint-plugin-jsdoc' import reactPlugin from 'eslint-plugin-react' import reactHooksPlugin from 'eslint-plugin-react-hooks' import neostandard from 'neostandard' +import reactCompilerPlugin from 'eslint-plugin-react-compiler' /** Files to include in linting. */ const filesToParse = ['**/*.{mjs,js,ts,tsx}'] @@ -86,12 +87,14 @@ export default defineConfig([ files: filesToParse, plugins: { react: reactPlugin, + 'react-compiler': reactCompilerPlugin }, languageOptions: { parserOptions: { ecmaFeatures: { jsx: true, }, + 'react-compiler/react-compiler': 'error', }, globals: { ...globals.browser, diff --git a/src/frontend/static/frontend/package-lock.json b/src/frontend/static/frontend/package-lock.json index 1e714c0b..82b5881d 100644 --- a/src/frontend/static/frontend/package-lock.json +++ b/src/frontend/static/frontend/package-lock.json @@ -16,20 +16,25 @@ "@visx/tooltip": "^3.3.0", "apexcharts": "^3.52.0", "axios": "^1.8.4", - "cytoscape": "^3.30.2", + "cytoscape": "^3.33.1", "d3": "^7.9.0", "dayjs": "^1.11.13", "fomantic-ui-css": "^2.9.4", - "ky": "^1.7.5", + "ky": "2.0.2", "lodash": "^4.17.21", "neo-react-semantic-ui-range": "^0.3.6", + "plotly.js": "^3.3.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-avatar": "^5.0.3", "react-colorful": "^5.6.1", "react-dom": "^18.3.1", + "react-intl": "^6.8.9", + "react-markdown": "^10.1.0", + "react-plotly.js": "^2.6.0", "react-virtualized": "^9.22.6", "recharts": "^2.15.1", + "remark-gfm": "^4.0.1", "semantic-ui-react": "^2.1.5", "spark-md5": "^3.0.2", "typeface-roboto": "1.1.13", @@ -37,6 +42,11 @@ "validator": "^13.15.0" }, "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/preset-env": "^7.27.2", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.27.0", @@ -47,18 +57,22 @@ "@types/react-dom": "^18.3.5", "@types/react-virtualized": "^9.22.2", "@types/validator": "^13.12.3", + "babel-loader": "^10.0.0", + "babel-plugin-react-compiler": "^19.1.0-rc.2", "css-loader": "^7.1.2", "eslint": "^9.27.0", "eslint-plugin-jsdoc": "^50.6.9", "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-compiler": "^19.1.0-rc.2", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^16.0.0", "neostandard": "^0.12.1", + "react-compiler-runtime": "^19.1.0-rc.2", "style-loader": "^4.0.0", "ts-checker-rspack-plugin": "^1.1.1", "ts-loader": "^9.5.2", - "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.59.3", "url-loader": "^4.1.1", "webpack-bundle-tracker": "^3.1.1" } @@ -104,33 +118,36 @@ "license": "ISC" }, "node_modules/@babel/compat-data": { - "version": "7.21.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", - "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.5.tgz", - "integrity": "sha512-9M398B/QH5DlfCOTKDZT1ozXr0x8uBEeFd+dJraGUZGiaNpGCDVGCc14hZexsMblw3XxltJ+6kSvogp9J+5a9g==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.5", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -140,6 +157,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/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==", + "license": "MIT" + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -150,15 +173,15 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -166,43 +189,31 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", - "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", - "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { @@ -228,19 +239,18 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.5.tgz", - "integrity": "sha512-yNSEck9SuDvPTEUYm4BSXl6ZVC7yO5ZLEMAhG3v3zi7RDxyL/nQDemWWZmw4L0stPWwhpnznRRyJHPRcbXR2jw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -259,13 +269,14 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.5.tgz", - "integrity": "sha512-1+DPMcln46eNAta/rPIqQYXYRGvQ/LRy6bRKnSt9Dzt/yLjNUbbsh+6yzD6fUHmtzc9kWvVnAhtcMSMyziHmUA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -308,105 +319,88 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", - "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dependencies": { - "@babel/types": "^7.18.6" - }, + "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==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz", - "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.21.4" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", - "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -416,38 +410,30 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz", - "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", - "dependencies": { - "@babel/types": "^7.21.5" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.0" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -483,35 +469,36 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" @@ -531,12 +518,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -545,12 +532,14 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -559,125 +548,76 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.12.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", - "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.21.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-json-strings": { + "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -686,13 +626,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", + "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -731,39 +674,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-optional-chaining": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", @@ -796,15 +706,10 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -812,21 +717,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -860,20 +750,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-decorators": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", @@ -888,28 +764,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-flow": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", @@ -925,11 +779,27 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -961,11 +831,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1040,10 +911,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { + "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1054,12 +925,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1068,26 +940,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1096,14 +971,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1112,12 +988,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1126,12 +1005,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1140,20 +1020,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1162,21 +1035,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1185,27 +1051,34 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", + "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1214,12 +1087,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1228,13 +1103,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1243,13 +1119,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1258,12 +1135,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1272,28 +1150,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1302,12 +1181,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1316,13 +1197,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1331,14 +1212,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1347,15 +1227,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", + "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/plugin-syntax-flow": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1364,13 +1242,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1379,27 +1258,30 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1408,13 +1290,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1423,12 +1305,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", - "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1437,12 +1320,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1451,12 +1335,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", - "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1465,12 +1351,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1479,16 +1367,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz", - "integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.21.5" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1497,12 +1385,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1511,28 +1401,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1541,12 +1432,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1555,17 +1447,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", - "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1574,21 +1462,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -1597,13 +1481,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1612,12 +1497,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1626,12 +1512,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1640,12 +1528,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1654,15 +1543,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1671,12 +1559,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1685,13 +1576,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1700,86 +1591,141 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", - "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", - "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", - "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.21.0", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.21.0", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.20.11", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.21.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.21.5", + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz", + "integrity": "sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", + "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", + "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", + "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", + "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", + "dependencies": { + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-plugin-utils": "^7.20.2", "babel-plugin-polyfill-corejs2": "^0.3.3", "babel-plugin-polyfill-corejs3": "^0.6.0", "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -1789,6 +1735,332 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", + "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.0", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.0", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1799,31 +2071,31 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", + "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.27.1", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/plugin-transform-react-jsx-development": "^7.27.1", + "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1833,15 +2105,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1850,16 +2123,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "node_modules/@babel/runtime": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -1879,35 +2148,27 @@ } }, "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1922,6 +2183,24 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@choojs/findup": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", + "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", + "license": "MIT", + "dependencies": { + "commander": "^2.15.1" + }, + "bin": { + "findup": "bin/findup.js" + } + }, + "node_modules/@choojs/findup/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -2026,9 +2305,10 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2054,9 +2334,10 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2242,52 +2523,179 @@ "node": ">= 0.8.0" } }, - "node_modules/@eslint/plugin-kit/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==", - "license": "MIT", - "engines": { - "node": ">= 0.8.0" + "node_modules/@eslint/plugin-kit/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==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@eslint/plugin-kit/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==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@fluentui/react-component-event-listener": { + "version": "0.63.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz", + "integrity": "sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==", + "dependencies": { + "@babel/runtime": "^7.10.4" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/@fluentui/react-component-ref": { + "version": "0.63.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz", + "integrity": "sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==", + "dependencies": { + "@babel/runtime": "^7.10.4", + "react-is": "^16.6.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz", + "integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==", + "dependencies": { + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/ecma402-abstract/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==" + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz", + "integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==", + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@formatjs/fast-memoize/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==" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz", + "integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/icu-skeleton-parser": "1.8.8", + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/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==" + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz", + "integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/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==" + }, + "node_modules/@formatjs/intl": { + "version": "2.10.15", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz", + "integrity": "sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g==", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/icu-messageformat-parser": "2.9.4", + "@formatjs/intl-displaynames": "6.8.5", + "@formatjs/intl-listformat": "7.7.5", + "intl-messageformat": "10.7.7", + "tslib": "2" + }, + "peerDependencies": { + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@eslint/plugin-kit/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==", - "license": "MIT", + "node_modules/@formatjs/intl-displaynames": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz", + "integrity": "sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w==", "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" } }, - "node_modules/@fluentui/react-component-event-listener": { - "version": "0.63.1", - "resolved": "https://registry.npmjs.org/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz", - "integrity": "sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==", + "node_modules/@formatjs/intl-displaynames/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==" + }, + "node_modules/@formatjs/intl-listformat": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz", + "integrity": "sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg==", "dependencies": { - "@babel/runtime": "^7.10.4" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/intl-localematcher": "0.5.8", + "tslib": "2" } }, - "node_modules/@fluentui/react-component-ref": { - "version": "0.63.1", - "resolved": "https://registry.npmjs.org/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz", - "integrity": "sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==", + "node_modules/@formatjs/intl-listformat/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==" + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz", + "integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==", "dependencies": { - "@babel/runtime": "^7.10.4", - "react-is": "^16.6.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" + "tslib": "2" } }, + "node_modules/@formatjs/intl-localematcher/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==" + }, + "node_modules/@formatjs/intl/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==" + }, "node_modules/@gamestdio/websocket": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@gamestdio/websocket/-/websocket-0.3.2.tgz", @@ -3369,17 +3777,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -3390,15 +3794,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", @@ -3409,14 +3804,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3433,6 +3830,122 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==", + "license": "ISC" + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", + "license": "BSD-3-Clause", + "peerDependencies": { + "mapbox-gl": ">=0.32.1 <2.0.0" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", + "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", + "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, "node_modules/@module-federation/error-codes": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.11.2.tgz", @@ -3573,6 +4086,92 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@plotly/d3": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@plotly/d3/-/d3-3.8.2.tgz", + "integrity": "sha512-wvsNmh1GYjyJfyEBPKJLTMzgf2c2bEbSIL50lmqVUi+o1NHaLPi1Lb4v7VxXXJn043BhNyrxUrWI85Q+zmjOVA==", + "license": "BSD-3-Clause" + }, + "node_modules/@plotly/d3-sankey": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz", + "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1", + "d3-collection": "1", + "d3-shape": "^1.2.0" + } + }, + "node_modules/@plotly/d3-sankey-circular": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", + "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", + "license": "MIT", + "dependencies": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.4", + "d3-shape": "^1.2.0", + "elementary-circuits-directed-graph": "^1.0.4" + } + }, + "node_modules/@plotly/mapbox-gl": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@plotly/mapbox-gl/-/mapbox-gl-1.13.4.tgz", + "integrity": "sha512-sR3/Pe5LqT/fhYgp4rT4aSFf1rTsxMbGiH6Hojc7PH36ny5Bn17iVFUjpzycafETURuFbLZUfjODO8LvSI+5zQ==", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.1.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/@plotly/point-cluster": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@plotly/point-cluster/-/point-cluster-3.1.9.tgz", + "integrity": "sha512-MwaI6g9scKf68Orpr1pHZ597pYx9uP8UEFXLPbsCmuw3a84obwz6pnMXGc90VhgDNeNiLEdlmuK7CPo+5PIxXw==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "binary-search-bounds": "^2.0.4", + "clamp": "^1.0.1", + "defined": "^1.0.0", + "dtype": "^2.0.0", + "flatten-vertex-data": "^1.0.2", + "is-obj": "^1.0.1", + "math-log2": "^1.0.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0" + } + }, + "node_modules/@plotly/regl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@plotly/regl/-/regl-2.1.2.tgz", + "integrity": "sha512-Mdk+vUACbQvjd0m/1JJjOOafmkp/EpmHjISsopEz5Av44CBq7rPC05HHNbYGKVyNUF2zmEoBS/TT0pd0SPFFyw==", + "license": "MIT" + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz", @@ -3637,6 +4236,7 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -3955,6 +4555,7 @@ "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.3.2.tgz", "integrity": "sha512-QbEn1SkNW3b89KTlSkp6OHdvw3DhpL6tSdDhsOlldw3LoRBy4fx80Z9W9lmg+g+8DjTAs1Z1ysElEFtAN69AZg==", "dev": true, + "peer": true, "dependencies": { "@module-federation/runtime-tools": "0.11.2", "@rspack/binding": "1.3.2", @@ -4005,6 +4606,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4635,31 +5237,107 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "dev": true, - "optional": true, - "peer": true, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@turf/area": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.3.1.tgz", + "integrity": "sha512-9nSiwt4zB5QDMcSoTxF28WpK1f741MNKcpUJDiHVRX08CZ4qfGWGV9ZIPQ8TVEn5RE4LyYkFuQ47Z9pdEUZE9Q==", + "license": "MIT", "dependencies": { - "tslib": "^2.8.0" + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, - "node_modules/@swc/helpers/node_modules/tslib": { + "node_modules/@turf/area/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, - "optional": true, - "peer": true + "license": "0BSD" }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" + "node_modules/@turf/bbox": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.3.1.tgz", + "integrity": "sha512-/IyMKoS7P9B0ch5PIlQ6gMfoE8gRr48+cSbzlyexvEjuDuaAV1VURjH1jAthS0ipFG8RrFxFJKnp7TLL1Skong==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox/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==", + "license": "0BSD" + }, + "node_modules/@turf/centroid": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.3.1.tgz", + "integrity": "sha512-hRnsDdVBH4pX9mAjYympb2q5W8TCMUMNEjcRrAF7HTCyjIuRmjJf8vUtlzf7TTn9RXbsvPc1vtm3kLw20Jm8DQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@turf/meta": "7.3.1", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid/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==", + "license": "0BSD" + }, + "node_modules/@turf/helpers": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.1.tgz", + "integrity": "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers/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==", + "license": "0BSD" + }, + "node_modules/@turf/meta": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.3.1.tgz", + "integrity": "sha512-NWsfOE5RVtWpLQNkfOF/RrYvLRPwwruxhZUV0UFIzHqfiRJ50aO9Y6uLY4bwCUe2TumLJQSR4yaoA72Rmr2mnQ==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "7.3.1", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, "node_modules/@tybys/wasm-util": { @@ -4994,6 +5672,15 @@ "@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/doctrine": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", @@ -5009,21 +5696,21 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "peer": true, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "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/eslint": "*", "@types/estree": "*" } }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" - }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -5065,6 +5752,15 @@ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -5082,6 +5778,26 @@ "@types/node": "*" } }, + "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/hoist-non-react-statics": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -5143,15 +5859,42 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==" }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", + "license": "MIT" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, "node_modules/@types/markdown-it": { "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "peer": true, "dependencies": { "@types/linkify-it": "*", "@types/mdurl": "*" } }, + "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/mdurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", @@ -5173,6 +5916,12 @@ "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" }, + "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": "12.20.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", @@ -5197,6 +5946,12 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", + "license": "MIT" + }, "node_modules/@types/prettier": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", @@ -5228,6 +5983,7 @@ "version": "18.3.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -5319,6 +6075,15 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/tapable": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz", @@ -5340,6 +6105,12 @@ "node": ">=0.10.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/@types/validator": { "version": "13.12.3", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.3.tgz", @@ -5350,6 +6121,7 @@ "version": "4.41.32", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz", "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==", + "peer": true, "dependencies": { "@types/node": "*", "@types/tapable": "^1", @@ -5408,20 +6180,20 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz", - "integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", + "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/type-utils": "8.28.0", - "@typescript-eslint/utils": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/type-utils": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5431,19 +6203,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "@typescript-eslint/parser": "^8.59.3", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5453,17 +6226,29 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "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.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz", - "integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/typescript-estree": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5473,18 +6258,38 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5492,6 +6297,9 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { @@ -5546,16 +6354,35 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", + "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "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.1.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz", - "integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", + "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.28.0", - "@typescript-eslint/utils": "8.28.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5565,15 +6392,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", - "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", + "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5583,19 +6411,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", - "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", + "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/project-service": "8.59.3", + "@typescript-eslint/tsconfig-utils": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5605,43 +6435,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.1.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": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", - "integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", + "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/typescript-estree": "8.28.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5651,18 +6497,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5673,13 +6520,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", - "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", + "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.59.3", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5690,17 +6538,24 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "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": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "license": "ISC" + }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.2.tgz", @@ -6168,7 +7023,7 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -6179,13 +7034,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.9.0", @@ -6318,6 +7173,12 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==", + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -6334,6 +7195,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6413,6 +7275,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6572,6 +7435,7 @@ "version": "3.52.0", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.52.0.tgz", "integrity": "sha512-7dg0ADKs8AA89iYMZMe2sFDG0XK5PfqllKV9N+i3hKHm3vEtdhwz8AlXGm+/b0nJ6jKiaXsqci5LfVxNhtB+dA==", + "peer": true, "dependencies": { "@yr/monotone-cubic-spline": "^1.0.3", "svg.draggable.js": "^2.2.2", @@ -6638,6 +7502,12 @@ "node": ">=0.10.0" } }, + "node_modules/array-bounds": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz", + "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==", + "license": "MIT" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -6653,6 +7523,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -6677,6 +7556,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-normalize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", + "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.0" + } + }, + "node_modules/array-range": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz", + "integrity": "sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA==", + "license": "MIT" + }, + "node_modules/array-rearrange": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz", + "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==", + "license": "MIT" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -7029,6 +7929,7 @@ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/parser": "^7.7.0", @@ -7141,57 +8042,20 @@ } }, "node_modules/babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", + "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", + "dev": true, + "license": "MIT", "dependencies": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "schema-utils": "^2.6.5" + "find-up": "^5.0.0" }, "engines": { - "node": ">= 6.9" + "node": "^18.20.0 || ^20.10.0 || >=22.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/babel-loader/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "@babel/core": "^7.12.0", + "webpack": ">=5.61.0" } }, "node_modules/babel-plugin-istanbul": { @@ -7306,6 +8170,16 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-react-compiler": { + "version": "19.1.0-rc.2", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.1.0-rc.2.tgz", + "integrity": "sha512-kSNA//p5fMO6ypG8EkEVPIqAjwIXm5tMjfD1XRPL/sRjYSbJ6UsvORfaeolNWnZ9n310aM0xJP7peW26BuCVzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + } + }, "node_modules/babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", @@ -7414,6 +8288,16 @@ "babylon": "bin/babylon.js" } }, + "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", @@ -7482,6 +8366,15 @@ "node": ">=0.10.0" } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -7501,6 +8394,18 @@ } ] }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -7537,6 +8442,12 @@ "node": ">=8" } }, + "node_modules/binary-search-bounds": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", + "integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==", + "license": "MIT" + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -7546,6 +8457,28 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==", + "license": "MIT" + }, + "node_modules/bitmap-sdf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.4.tgz", + "integrity": "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -7804,9 +8737,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "funding": [ { "type": "opencollective", @@ -7821,11 +8754,14 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "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" @@ -8100,9 +9036,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001707", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", - "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "funding": [ { "type": "opencollective", @@ -8116,7 +9052,17 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-fit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", + "integrity": "sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==", + "license": "MIT", + "dependencies": { + "element-size": "^1.1.1" + } }, "node_modules/capture-exit": { "version": "2.0.0", @@ -8148,6 +9094,16 @@ "node": ">= 10" } }, + "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": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -8169,6 +9125,46 @@ "node": ">=10" } }, + "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/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -8241,6 +9237,12 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==" }, + "node_modules/clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==", + "license": "MIT" + }, "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -8364,6 +9366,24 @@ "color-string": "^1.6.0" } }, + "node_modules/color-alpha": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz", + "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==", + "license": "MIT", + "dependencies": { + "color-parse": "^1.3.8" + } + }, + "node_modules/color-alpha/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8372,11 +9392,75 @@ "color-name": "1.1.3" } }, + "node_modules/color-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz", + "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==", + "license": "MIT", + "dependencies": { + "clamp": "^1.0.1" + } + }, "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-normalize": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz", + "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==", + "license": "MIT", + "dependencies": { + "clamp": "^1.0.1", + "color-rgba": "^2.1.1", + "dtype": "^2.0.0" + } + }, + "node_modules/color-normalize/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-normalize/node_modules/color-rgba": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.4.0.tgz", + "integrity": "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==", + "license": "MIT", + "dependencies": { + "color-parse": "^1.4.2", + "color-space": "^2.0.0" + } + }, + "node_modules/color-parse": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-2.0.0.tgz", + "integrity": "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/color-rgba": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-3.0.0.tgz", + "integrity": "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg==", + "license": "MIT", + "dependencies": { + "color-parse": "^2.0.0", + "color-space": "^2.0.0" + } + }, + "node_modules/color-space": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.3.2.tgz", + "integrity": "sha512-BcKnbOEsOarCwyoLstcoEztwT0IJxqqQkNwDuA3a65sICvvHL2yoeV13psoDFh5IuiOMnIOKdQDwB4Mk3BypiA==", + "license": "Unlicense" + }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", @@ -8403,6 +9487,16 @@ "node": ">= 0.8" } }, + "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": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -8639,11 +9733,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.1.tgz", - "integrity": "sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==", + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", + "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.5" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -8651,11 +9746,11 @@ } }, "node_modules/core-js-pure": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.23.2.tgz", - "integrity": "sha512-t6u7H4Ff/yZNk+zqTr74UjCcZ3k8ApBryeLLV4rYQd9aF3gqmjjGjjR44ENfeBMH8VVvSynIjAJ0mUuFhzQtrA==", - "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz", + "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==", "hasInstallScript": true, + "license": "MIT", "peer": true, "funding": { "type": "opencollective", @@ -8682,6 +9777,12 @@ "node": ">=10" } }, + "node_modules/country-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", + "integrity": "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA==", + "license": "MIT" + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -8828,6 +9929,53 @@ "node": ">4" } }, + "node_modules/css-font": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz", + "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==", + "license": "MIT", + "dependencies": { + "css-font-size-keywords": "^1.0.0", + "css-font-stretch-keywords": "^1.0.1", + "css-font-style-keywords": "^1.0.1", + "css-font-weight-keywords": "^1.0.0", + "css-global-keywords": "^1.0.1", + "css-system-font-keywords": "^1.0.0", + "pick-by-alias": "^1.2.0", + "string-split-by": "^1.0.0", + "unquote": "^1.1.0" + } + }, + "node_modules/css-font-size-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz", + "integrity": "sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==", + "license": "MIT" + }, + "node_modules/css-font-stretch-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz", + "integrity": "sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==", + "license": "MIT" + }, + "node_modules/css-font-style-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz", + "integrity": "sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==", + "license": "MIT" + }, + "node_modules/css-font-weight-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz", + "integrity": "sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==", + "license": "MIT" + }, + "node_modules/css-global-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz", + "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ==", + "license": "MIT" + }, "node_modules/css-has-pseudo": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", @@ -8939,6 +10087,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -9054,6 +10203,12 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-system-font-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", + "integrity": "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==", + "license": "MIT" + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -9093,6 +10248,12 @@ "node": ">=0.10.0" } }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "license": "MIT" + }, "node_modules/cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -9312,9 +10473,10 @@ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==" }, "node_modules/cytoscape": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.2.tgz", - "integrity": "sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -9368,6 +10530,12 @@ "node": ">=12" } }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==", + "license": "BSD-3-Clause" + }, "node_modules/d3-axis": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", @@ -9402,6 +10570,12 @@ "node": ">=12" } }, + "node_modules/d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", + "license": "BSD-3-Clause" + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -9555,6 +10729,40 @@ "node": ">=12" } }, + "node_modules/d3-geo-projection": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-2.9.0.tgz", + "integrity": "sha512-ZULvK/zBn87of5rWAfFMc9mJOipeSo57O+BBitsKIXmU4rTVAnX1kSsJkE0R+TxY8pGNoM1nbyRRE7GYHhdOEQ==", + "license": "BSD-3-Clause", + "dependencies": { + "commander": "2", + "d3-array": "1", + "d3-geo": "^1.12.0", + "resolve": "^1.1.10" + }, + "bin": { + "geo2svg": "bin/geo2svg", + "geograticule": "bin/geograticule", + "geoproject": "bin/geoproject", + "geoquantize": "bin/geoquantize", + "geostitch": "bin/geostitch" + } + }, + "node_modules/d3-geo-projection/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/d3-geo-projection/node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1" + } + }, "node_modules/d3-geo/node_modules/d3-array": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz", @@ -9631,6 +10839,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "peer": true, "engines": { "node": ">=12" } @@ -9854,9 +11063,10 @@ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "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" }, @@ -9918,6 +11128,19 @@ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" }, + "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/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -10123,6 +11346,15 @@ "node": ">=0.10.0" } }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/del": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", @@ -10187,6 +11419,15 @@ "node": ">= 0.8" } }, + "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/des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -10205,6 +11446,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-kerning": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz", + "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==", + "license": "MIT" + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -10247,6 +11494,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "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/diff-sequences": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", @@ -10456,6 +11716,25 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/draw-svg-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz", + "integrity": "sha512-P8j3IHxcgRMcY6sDzr0QvJDLzBnJJqpTG33UZ2Pvp8rw0apCHhJCWqYprqrXjrgHnJ6tuhP1iTJSAodPDHxwkg==", + "license": "MIT", + "dependencies": { + "abs-svg-path": "~0.1.1", + "normalize-svg-path": "~0.1.0" + } + }, + "node_modules/dtype": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", + "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -10469,6 +11748,12 @@ "node": ">= 0.4" } }, + "node_modules/dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==", + "license": "MIT" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -10485,6 +11770,12 @@ "stream-shift": "^1.0.0" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "license": "ISC" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10500,9 +11791,25 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.123", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", - "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==" + "version": "1.5.372", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", + "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", + "license": "ISC" + }, + "node_modules/element-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", + "integrity": "sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==", + "license": "MIT" + }, + "node_modules/elementary-circuits-directed-graph": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.3.1.tgz", + "integrity": "sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==", + "license": "MIT", + "dependencies": { + "strongly-connected-components": "^1.0.1" + } }, "node_modules/elliptic": { "version": "6.6.1", @@ -10757,7 +12064,8 @@ "node_modules/es-module-lexer": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==" + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -10846,6 +12154,18 @@ "ext": "^1.1.2" } }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -10868,14 +12188,14 @@ } }, "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", @@ -10910,6 +12230,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -11100,6 +12421,7 @@ "version": "2.31.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -11133,6 +12455,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.9.3.tgz", "integrity": "sha512-NrPUarxpFzGpQVXdVWkGttDD8WIxBuM/dRNw5kKFxrlGdjAJ3l8ma0LK5hsK5Qp79GBGM+HY1zYVbHqateTklA==", "dev": true, + "peer": true, "dependencies": { "@types/doctrine": "^0.0.9", "@typescript-eslint/utils": "^8.28.0", @@ -11303,6 +12626,7 @@ "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -11336,6 +12660,7 @@ "version": "7.37.4", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -11363,6 +12688,27 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-compiler": { + "version": "19.1.0-rc.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.2.tgz", + "integrity": "sha512-oKalwDGcD+RX9mf3NEO4zOoUMeLvjSvcbbEOpquzmzqEEM2MQdp7/FY/Hx9NzmUwFzH1W9SKTz5fihfMldpEYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "hermes-parser": "^0.25.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", @@ -11815,6 +13161,16 @@ "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/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -12176,6 +13532,12 @@ "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" }, + "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/extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -12274,6 +13636,25 @@ "node": ">=0.10.0" } }, + "node_modules/falafel": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz", + "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==", + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "isarray": "^2.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/falafel/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "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", @@ -12302,6 +13683,15 @@ "node": ">=8.6.0" } }, + "node_modules/fast-isnumeric": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.4.tgz", + "integrity": "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw==", + "license": "MIT", + "dependencies": { + "is-string-blank": "^1.0.1" + } + }, "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", @@ -12461,19 +13851,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -12512,6 +13889,15 @@ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash." }, + "node_modules/flatten-vertex-data": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", + "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==", + "license": "MIT", + "dependencies": { + "dtype": "^2.0.0" + } + }, "node_modules/flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -12540,12 +13926,30 @@ } } }, - "node_modules/fomantic-ui-css": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/fomantic-ui-css/-/fomantic-ui-css-2.9.4.tgz", - "integrity": "sha512-4U8enaBjxAdjMfpTKhf0Of9/XZJo/WSh90FaF85gIYy669eGYe0tRFzE9T0lIiLRmxa20yKLhT6TvLxGau13dA==", + "node_modules/fomantic-ui-css": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/fomantic-ui-css/-/fomantic-ui-css-2.9.4.tgz", + "integrity": "sha512-4U8enaBjxAdjMfpTKhf0Of9/XZJo/WSh90FaF85gIYy669eGYe0tRFzE9T0lIiLRmxa20yKLhT6TvLxGau13dA==", + "dependencies": { + "jquery": "^3.4.0" + } + }, + "node_modules/font-atlas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz", + "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==", + "license": "MIT", + "dependencies": { + "css-font": "^1.0.0" + } + }, + "node_modules/font-measure": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz", + "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==", + "license": "MIT", "dependencies": { - "jquery": "^3.4.0" + "css-font": "^1.2.0" } }, "node_modules/for-each": { @@ -12870,6 +14274,12 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", + "license": "ISC" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -12878,6 +14288,12 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-canvas-context": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz", + "integrity": "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==", + "license": "MIT" + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -12976,6 +14392,58 @@ "node": ">=0.10.0" } }, + "node_modules/gl-mat4": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", + "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==", + "license": "Zlib" + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "node_modules/gl-text": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.4.0.tgz", + "integrity": "sha512-o47+XBqLCj1efmuNyCHt7/UEJmB9l66ql7pnobD6p+sgmBUdzfMZXIF0zD2+KRfpd99DJN+QXdvTFAGCKCVSmQ==", + "license": "MIT", + "dependencies": { + "bit-twiddle": "^1.0.2", + "color-normalize": "^1.5.0", + "css-font": "^1.2.0", + "detect-kerning": "^2.1.2", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "font-atlas": "^2.1.0", + "font-measure": "^1.2.2", + "gl-util": "^3.1.2", + "is-plain-obj": "^1.1.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "parse-unit": "^1.0.1", + "pick-by-alias": "^1.2.0", + "regl": "^2.0.0", + "to-px": "^1.0.1", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-util": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.3.tgz", + "integrity": "sha512-dvRTggw5MSkJnCbh74jZzSoTOGnVYK+Bt+Ckqm39CVcl6+zSsxqWk4lr5NKhkqXHL6qvZAU9h17ZF8mIskY9mA==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1", + "is-firefox": "^1.0.3", + "is-plain-obj": "^1.1.0", + "number-is-integer": "^1.0.1", + "object-assign": "^4.1.0", + "pick-by-alias": "^1.2.0", + "weak-map": "^1.0.5" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -13010,7 +14478,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/global-modules": { "version": "2.0.0", @@ -13082,6 +14550,207 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glsl-inject-defines": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz", + "integrity": "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==", + "license": "MIT", + "dependencies": { + "glsl-token-inject-block": "^1.0.0", + "glsl-token-string": "^1.0.1", + "glsl-tokenizer": "^2.0.2" + } + }, + "node_modules/glsl-resolve": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz", + "integrity": "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA==", + "license": "MIT", + "dependencies": { + "resolve": "^0.6.1", + "xtend": "^2.1.2" + } + }, + "node_modules/glsl-resolve/node_modules/resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==", + "license": "MIT" + }, + "node_modules/glsl-resolve/node_modules/xtend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/glsl-token-assignments": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz", + "integrity": "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ==", + "license": "MIT" + }, + "node_modules/glsl-token-defines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz", + "integrity": "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ==", + "license": "MIT", + "dependencies": { + "glsl-tokenizer": "^2.0.0" + } + }, + "node_modules/glsl-token-depth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz", + "integrity": "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg==", + "license": "MIT" + }, + "node_modules/glsl-token-descope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz", + "integrity": "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw==", + "license": "MIT", + "dependencies": { + "glsl-token-assignments": "^2.0.0", + "glsl-token-depth": "^1.1.0", + "glsl-token-properties": "^1.0.0", + "glsl-token-scope": "^1.1.0" + } + }, + "node_modules/glsl-token-inject-block": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz", + "integrity": "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA==", + "license": "MIT" + }, + "node_modules/glsl-token-properties": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz", + "integrity": "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA==", + "license": "MIT" + }, + "node_modules/glsl-token-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz", + "integrity": "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A==", + "license": "MIT" + }, + "node_modules/glsl-token-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz", + "integrity": "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg==", + "license": "MIT" + }, + "node_modules/glsl-token-whitespace-trim": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz", + "integrity": "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz", + "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", + "license": "MIT", + "dependencies": { + "through2": "^0.6.3" + } + }, + "node_modules/glsl-tokenizer/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/glsl-tokenizer/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/glsl-tokenizer/node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "license": "MIT", + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/glslify": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.1.1.tgz", + "integrity": "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog==", + "license": "MIT", + "dependencies": { + "bl": "^2.2.1", + "concat-stream": "^1.5.2", + "duplexify": "^3.4.5", + "falafel": "^2.1.0", + "from2": "^2.3.0", + "glsl-resolve": "0.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glslify-bundle": "^5.0.0", + "glslify-deps": "^1.2.5", + "minimist": "^1.2.5", + "resolve": "^1.1.5", + "stack-trace": "0.0.9", + "static-eval": "^2.0.5", + "through2": "^2.0.1", + "xtend": "^4.0.0" + }, + "bin": { + "glslify": "bin.js" + } + }, + "node_modules/glslify-bundle": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz", + "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==", + "license": "MIT", + "dependencies": { + "glsl-inject-defines": "^1.0.1", + "glsl-token-defines": "^1.0.0", + "glsl-token-depth": "^1.1.1", + "glsl-token-descope": "^1.0.2", + "glsl-token-scope": "^1.1.1", + "glsl-token-string": "^1.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glsl-tokenizer": "^2.0.2", + "murmurhash-js": "^1.0.0", + "shallow-copy": "0.0.1" + } + }, + "node_modules/glslify-deps": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz", + "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==", + "license": "ISC", + "dependencies": { + "@choojs/findup": "^0.2.0", + "events": "^3.2.0", + "glsl-resolve": "0.0.1", + "glsl-tokenizer": "^2.0.0", + "graceful-fs": "^4.1.2", + "inherits": "^2.0.1", + "map-limit": "0.0.1", + "resolve": "^1.0.0" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -13098,11 +14767,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==" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", + "license": "ISC" }, "node_modules/growly": { "version": "1.3.0", @@ -13167,6 +14836,24 @@ "node": ">=4" } }, + "node_modules/has-hover": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-hover/-/has-hover-1.0.1.tgz", + "integrity": "sha512-0G6w7LnlcpyDzpeGUTuT0CEw05+QlMuGVk1IHNAlHrGJITGodjZu3x8BNDUMfKJSZXNB2ZAclqc1bvrd+uUpfg==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1" + } + }, + "node_modules/has-passive-events": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-passive-events/-/has-passive-events-1.0.0.tgz", + "integrity": "sha512-2vSj6IeIsgvsRMyeQ0JaCX5Q3lX4zMn5HpoVc7MEhQ6pv8Iq9rsXjsp+E5ZwaT7T0xhMT0KmU8gtt1EFVdbJiw==", + "license": "MIT", + "dependencies": { + "is-browser": "^2.0.1" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -13340,6 +15027,46 @@ "node": ">= 0.4" } }, + "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/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -13348,6 +15075,23 @@ "he": "bin/he" } }, + "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/hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", @@ -13363,6 +15107,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -13444,6 +15196,16 @@ "node": ">=6" } }, + "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/html-webpack-plugin": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", @@ -13961,6 +15723,12 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "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/internal-ip": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", @@ -14003,6 +15771,22 @@ "node": ">=10.13.0" } }, + "node_modules/intl-messageformat": { + "version": "10.7.7", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz", + "integrity": "sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/icu-messageformat-parser": "2.9.4", + "tslib": "2" + } + }, + "node_modules/intl-messageformat/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==" + }, "node_modules/ip": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", @@ -14055,6 +15839,30 @@ "node": ">=0.10.0" } }, + "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-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -14150,6 +15958,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", + "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==", + "license": "MIT" + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -14266,6 +16080,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -14339,6 +16163,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-firefox": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-firefox/-/is-firefox-1.0.3.tgz", + "integrity": "sha512-6Q9ITjvWIm0Xdqv+5U12wgOKEM2KoBw4Y926m0OFkvlCxnbG94HKAsVz8w3fWcfAS5YA2fJORXX1dLrkprCCxA==", + "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", @@ -14383,6 +16228,25 @@ "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-iexplorer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-iexplorer/-/is-iexplorer-1.0.0.tgz", + "integrity": "sha512-YeLzceuwg3K6O0MLM3UyUUjKAlyULetwryFp1mHy1I5PfArK0AEqlfa+MR4gkJjcbuJXoDJCvXbyqZVf5CR2Sg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -14427,6 +16291,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-mobile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-4.0.0.tgz", + "integrity": "sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew==", + "license": "MIT" + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -14631,6 +16501,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-string-blank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", + "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==", + "license": "MIT" + }, + "node_modules/is-svg-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-svg-path/-/is-svg-path-1.0.2.tgz", + "integrity": "sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg==", + "license": "MIT" + }, "node_modules/is-symbol": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", @@ -14883,6 +16765,7 @@ "version": "26.6.0", "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.0.tgz", "integrity": "sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA==", + "peer": true, "dependencies": { "@jest/core": "^26.6.0", "import-local": "^3.0.2", @@ -17149,21 +19032,11 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "optional": true, - "peer": true, - "bin": { - "jiti": "lib/jiti-cli.mjs" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jquery": { @@ -17343,6 +19216,12 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -17379,6 +19258,12 @@ "node": ">=4.0" } }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, "node_modules/keyboard-key": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.1.0.tgz", @@ -17431,11 +19316,12 @@ } }, "node_modules/ky": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.7.5.tgz", - "integrity": "sha512-HzhziW6sc5m0pwi5M196+7cEBtbt0lCYi67wNsiwMUmz833wloE0gbzJPWKs1gliFKQb34huItDQX97LyOdPdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-2.0.2.tgz", + "integrity": "sha512-/GmXpo9F9W+f8n4Ivr2iH+7h7wL7jLbLKWkMlpflcCRb6kGjBfTlASEXaZ9qUgNTn4VgS0P2pwxxzQ4EM6Ulgg==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=22" }, "funding": { "url": "https://github.com/sindresorhus/ky?sponsor=1" @@ -17502,18 +19388,6 @@ "node": ">=6" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -17668,6 +19542,16 @@ "url": "https://tidelift.com/funding/github/npm/loglevel" } }, + "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/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -17711,27 +19595,6 @@ "sourcemap-codec": "^1.4.8" } }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -17748,6 +19611,24 @@ "node": ">=0.10.0" } }, + "node_modules/map-limit": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", + "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==", + "license": "MIT", + "dependencies": { + "once": "~1.3.0" + } + }, + "node_modules/map-limit/node_modules/once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/map-obj": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", @@ -17770,10 +19651,184 @@ "node": ">=0.10.0" } }, + "node_modules/mapbox-gl": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", + "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", + "license": "SEE LICENSE IN LICENSE.txt", + "peer": true, + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.1.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/maplibre-gl": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", + "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "global-prefix": "^4.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.3.0", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/maplibre-gl/node_modules/@mapbox/tiny-sdf": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz", + "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/maplibre-gl/node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/global-prefix": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "license": "MIT", + "dependencies": { + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/maplibre-gl/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/maplibre-gl/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/maplibre-gl/node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/maplibre-gl/node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, + "node_modules/maplibre-gl/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", @@ -17794,6 +19849,16 @@ "markdown-it": "*" } }, + "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/marked": { "version": "4.0.17", "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz", @@ -17813,6 +19878,15 @@ "node": ">= 0.4" } }, + "node_modules/math-log2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", + "integrity": "sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -17828,9 +19902,291 @@ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "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/mdn-data": { @@ -17956,7 +20312,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/memory-fs": { "version": "0.4.1", @@ -18011,31 +20368,594 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "license": "MIT" + }, + "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/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" + "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/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" + "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/microevent.ts": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", - "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "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/micromatch": { @@ -18135,9 +21055,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -18262,6 +21186,38 @@ "node": ">=10" } }, + "node_modules/mouse-change": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz", + "integrity": "sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ==", + "license": "MIT", + "dependencies": { + "mouse-event": "^1.0.0" + } + }, + "node_modules/mouse-event": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz", + "integrity": "sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw==", + "license": "MIT" + }, + "node_modules/mouse-event-offset": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mouse-event-offset/-/mouse-event-offset-3.0.2.tgz", + "integrity": "sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w==", + "license": "MIT" + }, + "node_modules/mouse-wheel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz", + "integrity": "sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw==", + "license": "MIT", + "dependencies": { + "right-now": "^1.0.0", + "signum": "^1.0.0", + "to-px": "^1.0.1" + } + }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -18328,6 +21284,12 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==" }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, "node_modules/nan": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", @@ -18373,6 +21335,12 @@ "node": ">=0.10.0" } }, + "node_modules/native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==", + "license": "MIT" + }, "node_modules/native-url": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz", @@ -18386,6 +21354,32 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -18512,6 +21506,7 @@ "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "peer": true, "dependencies": { "@typescript-eslint/experimental-utils": "4.33.0", "@typescript-eslint/scope-manager": "4.33.0", @@ -18583,6 +21578,7 @@ "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", @@ -18667,29 +21663,184 @@ "acorn": "bin/acorn" }, "engines": { - "node": ">=0.4.0" + "node": ">=0.4.0" + } + }, + "node_modules/neo-react-semantic-ui-range/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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "license": "MIT", + "dependencies": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 6.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/neo-react-semantic-ui-range/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==", + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "find-up": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, - "node_modules/neo-react-semantic-ui-range/node_modules/arrify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", - "engines": { - "node": ">=8" + "node_modules/neo-react-semantic-ui-range/node_modules/babel-loader/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, "node_modules/neo-react-semantic-ui-range/node_modules/braces": { @@ -18822,6 +21973,7 @@ "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -18910,6 +22062,7 @@ "version": "5.10.0", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz", "integrity": "sha512-vcz32f+7TP+kvTUyMXZmCnNujBQZDNmcqPImw8b9PZ+16w1Qdm6ryRuYZYVaG9xRqqmAPr2Cs9FAX5gN+x/bjw==", + "peer": true, "dependencies": { "lodash": "^4.17.15", "string-natural-compare": "^3.0.1" @@ -18925,6 +22078,7 @@ "version": "24.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.7.0.tgz", "integrity": "sha512-wUxdF2bAZiYSKBclsUMrYHH6WxiBreNjyDxbRv345TIvPeoCEgPNEn3Sa+ZrSqsf1Dl9SqqSREXMHExlMMu1DA==", + "peer": true, "dependencies": { "@typescript-eslint/experimental-utils": "^4.0.1" }, @@ -18945,6 +22099,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "peer": true, "engines": { "node": ">=10" }, @@ -18956,6 +22111,7 @@ "version": "3.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz", "integrity": "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA==", + "peer": true, "dependencies": { "@typescript-eslint/experimental-utils": "^3.10.1" }, @@ -19455,6 +22611,7 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -19748,20 +22905,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/neo-react-semantic-ui-range/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/neo-react-semantic-ui-range/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -19774,6 +22917,7 @@ "version": "4.44.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.9.0", "@webassemblyjs/helper-module-context": "1.9.0", @@ -20377,9 +23521,13 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-package-data": { "version": "3.0.3", @@ -20411,6 +23559,12 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-svg-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", + "integrity": "sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA==", + "license": "MIT" + }, "node_modules/normalize-url": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", @@ -20460,6 +23614,18 @@ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==" }, + "node_modules/number-is-integer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-integer/-/number-is-integer-1.0.1.tgz", + "integrity": "sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg==", + "license": "MIT", + "dependencies": { + "is-finite": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nwsapi": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", @@ -20750,22 +23916,6 @@ "node": ">=4" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -20907,6 +24057,12 @@ "node": ">=6" } }, + "node_modules/parenthesis": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz", + "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==", + "license": "MIT" + }, "node_modules/parse-asn1": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", @@ -20957,6 +24113,31 @@ ], "license": "MIT" }, + "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/parse-imports": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", @@ -20987,6 +24168,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-rect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", + "integrity": "sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==", + "license": "MIT", + "dependencies": { + "pick-by-alias": "^1.2.0" + } + }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", + "license": "MIT" + }, + "node_modules/parse-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", + "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==", + "license": "MIT" + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -21079,6 +24281,19 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -21108,6 +24323,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "node_modules/pick-by-alias": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", + "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -21293,6 +24514,106 @@ "node": ">=4" } }, + "node_modules/plotly.js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-3.3.0.tgz", + "integrity": "sha512-3PT9dW7IbIfN7JWGr4YxxFQnbN5MRaB36qIKF/eF0iC9l0/MuGSlMlgRgI7Uu8vYuGxX6AjLwsBBRYTPG7NFSA==", + "license": "MIT", + "dependencies": { + "@plotly/d3": "3.8.2", + "@plotly/d3-sankey": "0.7.2", + "@plotly/d3-sankey-circular": "0.33.1", + "@plotly/mapbox-gl": "1.13.4", + "@plotly/regl": "^2.1.2", + "@turf/area": "^7.1.0", + "@turf/bbox": "^7.1.0", + "@turf/centroid": "^7.1.0", + "base64-arraybuffer": "^1.0.2", + "canvas-fit": "^1.5.0", + "color-alpha": "1.0.4", + "color-normalize": "1.5.0", + "color-parse": "2.0.0", + "color-rgba": "3.0.0", + "country-regex": "^1.1.0", + "d3-force": "^1.2.1", + "d3-format": "^1.4.5", + "d3-geo": "^1.12.1", + "d3-geo-projection": "^2.9.0", + "d3-hierarchy": "^1.1.9", + "d3-interpolate": "^3.0.1", + "d3-time": "^1.1.0", + "d3-time-format": "^2.2.3", + "fast-isnumeric": "^1.1.4", + "gl-mat4": "^1.2.0", + "gl-text": "^1.4.0", + "has-hover": "^1.0.1", + "has-passive-events": "^1.0.0", + "is-mobile": "^4.0.0", + "maplibre-gl": "^4.7.1", + "mouse-change": "^1.4.0", + "mouse-event-offset": "^3.0.2", + "mouse-wheel": "^1.2.0", + "native-promise-only": "^0.8.1", + "parse-svg-path": "^0.1.2", + "point-in-polygon": "^1.1.0", + "polybooljs": "^1.2.2", + "probe-image-size": "^7.2.3", + "regl-error2d": "^2.0.12", + "regl-line2d": "^3.1.3", + "regl-scatter2d": "^3.3.1", + "regl-splom": "^1.0.14", + "strongly-connected-components": "^1.0.1", + "superscript-text": "^1.0.0", + "svg-path-sdf": "^1.1.3", + "tinycolor2": "^1.4.2", + "to-px": "1.0.1", + "topojson-client": "^3.1.0", + "webgl-context": "^2.2.0", + "world-calendars": "^1.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/plotly.js/node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", + "license": "BSD-3-Clause" + }, + "node_modules/plotly.js/node_modules/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "node_modules/plotly.js/node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/plotly.js/node_modules/d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==", + "license": "BSD-3-Clause" + }, + "node_modules/plotly.js/node_modules/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==", + "license": "BSD-3-Clause" + }, "node_modules/pnp-webpack-plugin": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", @@ -21304,6 +24625,18 @@ "node": ">=6" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==", + "license": "MIT" + }, + "node_modules/polybooljs": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/polybooljs/-/polybooljs-1.2.2.tgz", + "integrity": "sha512-ziHW/02J0XuNuUtmidBc6GXE8YohYydp3DWPWXYsd7O721TjcmN+k6ezjdwkDqep+gnWnFY+yqZHvzElra2oCg==", + "license": "MIT" + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -22558,13 +25891,11 @@ "node": ">=0.10.0" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" }, "node_modules/prepend-http": { "version": "1.0.4", @@ -22643,6 +25974,17 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/probe-image-size": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-7.2.3.tgz", + "integrity": "sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==", + "license": "MIT", + "dependencies": { + "lodash.merge": "^4.6.2", + "needle": "^2.5.2", + "stream-parser": "~0.3.1" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -22693,12 +26035,29 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, + "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/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -22867,6 +26226,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "license": "ISC" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -22926,6 +26291,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -22985,6 +26351,16 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-compiler-runtime": { + "version": "19.1.0-rc.2", + "resolved": "https://registry.npmjs.org/react-compiler-runtime/-/react-compiler-runtime-19.1.0-rc.2.tgz", + "integrity": "sha512-852AwyIsbWJ5o1LkQVAZsVK3iLjMxOfKZuxqeGd/RfD+j1GqHb6j3DSHLtpu4HhFbQHsP2DzxjJyKR6luv4D8w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" + } + }, "node_modules/react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -23210,6 +26586,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -23236,6 +26613,37 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, + "node_modules/react-intl": { + "version": "6.8.9", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz", + "integrity": "sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg==", + "dependencies": { + "@formatjs/ecma402-abstract": "2.2.4", + "@formatjs/icu-messageformat-parser": "2.9.4", + "@formatjs/intl": "2.10.15", + "@formatjs/intl-displaynames": "6.8.5", + "@formatjs/intl-listformat": "7.7.5", + "@types/hoist-non-react-statics": "3", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "3", + "intl-messageformat": "10.7.7", + "tslib": "2" + }, + "peerDependencies": { + "react": "^16.6.0 || 17 || 18", + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-intl/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==" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -23246,6 +26654,46 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "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-plotly.js": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-plotly.js/-/react-plotly.js-2.6.0.tgz", + "integrity": "sha512-g93xcyhAVCSt9kV1svqG1clAEdL6k3U+jjuSzfTV7owaSU9Go6Ph8bl25J+jKfKvIGAEYpe4qj++WHJuc9IaeA==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "plotly.js": ">1.34.0", + "react": ">0.13.0" + } + }, "node_modules/react-popper": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", @@ -23264,6 +26712,7 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -23552,12 +27001,14 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -23570,14 +27021,6 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -23625,39 +27068,149 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/regl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/regl/-/regl-2.1.1.tgz", + "integrity": "sha512-+IOGrxl3FZ8ZM9ixCWQZzFRiRn7Rzn9bu3iFHwg/yz4tlOUQgbO4PHLgG+1ZT60zcIV8tief6Qrmyl8qcoJP0g==", + "license": "MIT" + }, + "node_modules/regl-error2d": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.12.tgz", + "integrity": "sha512-r7BUprZoPO9AbyqM5qlJesrSRkl+hZnVKWKsVp7YhOl/3RIpi4UDGASGJY0puQ96u5fBYw/OlqV24IGcgJ0McA==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "color-normalize": "^1.5.0", + "flatten-vertex-data": "^1.0.2", + "object-assign": "^4.1.1", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-line2d": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.3.tgz", + "integrity": "sha512-fkgzW+tTn4QUQLpFKsUIE0sgWdCmXAM3ctXcCgoGBZTSX5FE2A0M7aynz7nrZT5baaftLrk9te54B+MEq4QcSA==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "array-find-index": "^1.0.2", + "array-normalize": "^1.1.4", + "color-normalize": "^1.5.0", + "earcut": "^2.1.5", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0" + } + }, + "node_modules/regl-scatter2d": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.3.1.tgz", + "integrity": "sha512-seOmMIVwaCwemSYz/y4WE0dbSO9svNFSqtTh5RE57I7PjGo3tcUYKtH0MTSoshcAsreoqN8HoCtnn8wfHXXfKQ==", + "license": "MIT", + "dependencies": { + "@plotly/point-cluster": "^3.1.9", + "array-range": "^1.0.1", + "array-rearrange": "^2.2.2", + "clamp": "^1.0.1", + "color-id": "^1.1.0", + "color-normalize": "^1.5.0", + "color-rgba": "^2.1.1", + "flatten-vertex-data": "^1.0.2", + "glslify": "^7.0.0", + "is-iexplorer": "^1.0.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-scatter2d/node_modules/color-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.4.3.tgz", + "integrity": "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==", + "license": "MIT", "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" + "color-name": "^1.0.0" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/regl-scatter2d/node_modules/color-rgba": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.4.0.tgz", + "integrity": "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==", + "license": "MIT", "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" + "color-parse": "^1.4.2", + "color-space": "^2.0.0" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" + "node_modules/regl-splom": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.14.tgz", + "integrity": "sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw==", + "license": "MIT", + "dependencies": { + "array-bounds": "^1.0.1", + "array-range": "^1.0.1", + "color-alpha": "^1.0.4", + "flatten-vertex-data": "^1.0.2", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "raf": "^3.4.1", + "regl-scatter2d": "^3.2.3" } }, "node_modules/relateurl": { @@ -23668,6 +27221,72 @@ "node": ">= 0.10" } }, + "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/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -23802,6 +27421,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -23958,6 +27586,12 @@ "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==" }, + "node_modules/right-now": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz", + "integrity": "sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg==", + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -23990,6 +27624,7 @@ "version": "1.32.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "peer": true, "dependencies": { "@types/estree": "*", "@types/node": "*", @@ -24546,9 +28181,10 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -24787,6 +28423,12 @@ "sha.js": "bin.js" } }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", + "license": "MIT" + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -24895,6 +28537,12 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/signum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", + "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==", + "license": "MIT" + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -25135,6 +28783,7 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz", "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "peer": true, "dependencies": { "debug": "^3.2.7", "eventsource": "^2.0.2", @@ -25232,6 +28881,16 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "deprecated": "Please use @jridgewell/sourcemap-codec instead" }, + "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/spark-md5": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", @@ -25345,6 +29004,14 @@ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -25369,6 +29036,15 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, + "node_modules/static-eval": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.1.tgz", + "integrity": "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==", + "license": "MIT", + "dependencies": { + "escodegen": "^2.1.0" + } + }, "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -25430,6 +29106,30 @@ "xtend": "^4.0.0" } }, + "node_modules/stream-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", + "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", + "license": "MIT", + "dependencies": { + "debug": "2" + } + }, + "node_modules/stream-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/stream-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -25468,6 +29168,15 @@ "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" }, + "node_modules/string-split-by": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz", + "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==", + "license": "MIT", + "dependencies": { + "parenthesis": "^3.1.5" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -25582,6 +29291,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -25667,6 +29390,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strongly-connected-components": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", + "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA==", + "license": "MIT" + }, "node_modules/style-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", @@ -25683,6 +29412,24 @@ "webpack": "^5.27.0" } }, + "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/stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", @@ -25709,6 +29456,27 @@ "node": ">=8" } }, + "node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "license": "ISC", + "dependencies": { + "kdbush": "^3.0.0" + } + }, + "node_modules/supercluster/node_modules/kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", + "license": "ISC" + }, + "node_modules/superscript-text": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz", + "integrity": "sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -25762,11 +29530,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==", + "license": "ISC" + }, "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, + "node_modules/svg-path-bounds": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz", + "integrity": "sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ==", + "license": "MIT", + "dependencies": { + "abs-svg-path": "^0.1.1", + "is-svg-path": "^1.0.1", + "normalize-svg-path": "^1.0.0", + "parse-svg-path": "^0.1.2" + } + }, + "node_modules/svg-path-bounds/node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "license": "MIT", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, + "node_modules/svg-path-sdf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/svg-path-sdf/-/svg-path-sdf-1.1.3.tgz", + "integrity": "sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg==", + "license": "MIT", + "dependencies": { + "bitmap-sdf": "^1.0.0", + "draw-svg-path": "^1.0.0", + "is-svg-path": "^1.0.1", + "parse-svg-path": "^0.1.2", + "svg-path-bounds": "^1.0.1" + } + }, "node_modules/svg.draggable.js": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", @@ -26104,15 +29912,14 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "peer": true, + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -26126,22 +29933,49 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, "@swc/core": { "optional": true }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, "esbuild": { "optional": true }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, "uglify-js": { "optional": true } } }, "node_modules/terser-webpack-plugin/node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "peer": true, + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -26150,9 +29984,10 @@ } }, "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -26169,7 +30004,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "peer": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -26181,13 +30016,13 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "peer": true + "license": "MIT" }, "node_modules/terser-webpack-plugin/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==", - "peer": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -26196,7 +30031,7 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "peer": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -26210,13 +30045,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "peer": true + "license": "MIT" }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "peer": true, + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -26231,20 +30066,11 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/terser-webpack-plugin/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==", - "peer": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -26256,13 +30082,13 @@ } }, "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "peer": true, + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -26344,14 +30170,21 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, + "license": "MIT", "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -26361,10 +30194,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "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" }, @@ -26375,10 +30212,12 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "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", + "peer": true, "engines": { "node": ">=12" }, @@ -26395,6 +30234,12 @@ "node": "^18.0.0 || >=20.0.0" } }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "license": "ISC" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -26405,6 +30250,12 @@ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==" }, + "node_modules/to-float32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.1.0.tgz", + "integrity": "sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg==", + "license": "MIT" + }, "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -26427,6 +30278,15 @@ "node": ">=0.10.0" } }, + "node_modules/to-px": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.0.1.tgz", + "integrity": "sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw==", + "license": "MIT", + "dependencies": { + "parse-unit": "^1.0.1" + } + }, "node_modules/to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", @@ -26460,6 +30320,26 @@ "node": ">=0.6" } }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "license": "ISC", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -26503,6 +30383,16 @@ "node": ">=8" } }, + "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/trim-newlines": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz", @@ -26514,16 +30404,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "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" }, @@ -26772,17 +30673,6 @@ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -26791,19 +30681,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -26891,6 +30768,16 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/typedarray-pool": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.2.0.tgz", + "integrity": "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==", + "license": "MIT", + "dependencies": { + "bit-twiddle": "^1.0.0", + "dup": "^1.0.0" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -26905,9 +30792,11 @@ "integrity": "sha512-YXvbd3a1QTREoD+FJoEkl0VQNJoEjewR2H11IjVv4bp6ahuIcw0yyw/3udC4vJkHw3T3cUh85FTg8eWef3pSaw==" }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -26917,14 +30806,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.28.0.tgz", - "integrity": "sha512-jfZtxJoHm59bvoCMYCe2BM0/baMswRhMmYhy+w6VfcyHrjxZ0OJe0tGasydCpIpA+A/WIJhTyZfb3EtwNC/kHQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", + "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.28.0", - "@typescript-eslint/parser": "8.28.0", - "@typescript-eslint/utils": "8.28.0" + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -26934,8 +30825,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/uc.micro": { @@ -26966,9 +30857,10 @@ "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -26977,6 +30869,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -26986,9 +30879,10 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", "engines": { "node": ">=4" } @@ -26997,10 +30891,42 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", "engines": { "node": ">=4" } }, + "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/unified/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/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -27052,6 +30978,74 @@ "node": ">=4" } }, + "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.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -27153,9 +31147,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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==", "funding": [ { "type": "opencollective", @@ -27170,6 +31164,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -27184,7 +31179,14 @@ "node_modules/update-browserslist-db/node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/update-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz", + "integrity": "sha512-rCiBPiHxZwT4+sBhEbChzpO5hYHjm91kScWgdHf4Qeafs6Ba7MBl+d9GlGv72bcTZQO0sLmtQS1pHSWoCLtN/A==", + "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", @@ -27401,6 +31403,34 @@ "url": "https://github.com/sponsors/wooorm" } }, + "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/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", @@ -27491,6 +31521,17 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -27798,6 +31839,21 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/weak-map": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", + "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==", + "license": "Apache-2.0" + }, + "node_modules/webgl-context": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz", + "integrity": "sha512-q/fGIivtqTT7PEoF07axFIlHNk/XCPaYpq64btnepopSWvKNFkoORlQYgqDigBIuGA1ExnFd/GnSUnBNEPQY7Q==", + "license": "MIT", + "dependencies": { + "get-canvas-context": "^1.0.1" + } + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -27807,36 +31863,35 @@ } }, "node_modules/webpack": { - "version": "5.99.8", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz", - "integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==", + "version": "5.107.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", + "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", "license": "MIT", "peer": true, "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.22.0", + "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.5.0", + "watchpack": "^2.5.1", + "webpack-sources": "^3.5.0" }, "bin": { "webpack": "bin/webpack.js" @@ -28004,6 +32059,7 @@ "version": "3.11.1", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz", "integrity": "sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ==", + "peer": true, "dependencies": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", @@ -28698,7 +32754,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -28708,25 +32764,25 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -28738,7 +32794,7 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "peer": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -28747,7 +32803,7 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "peer": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -28756,13 +32812,13 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -28778,7 +32834,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -28791,7 +32847,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -28803,7 +32859,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -28817,16 +32873,17 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "peer": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, "node_modules/webpack/node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", "peer": true, "bin": { "acorn": "bin/acorn" @@ -28835,10 +32892,22 @@ "node": ">=0.4.0" } }, + "node_modules/webpack/node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "peer": true, "dependencies": { @@ -28857,7 +32926,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -28866,40 +32934,57 @@ } }, "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "peer": true, + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", + "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "license": "MIT" + }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "peer": true, + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -28915,21 +33000,24 @@ } }, "node_modules/webpack/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "peer": true, + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/webpack/node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "peer": true, + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "license": "MIT", "dependencies": { - "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" }, "engines": { @@ -28937,10 +33025,10 @@ } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "peer": true, + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -29319,6 +33407,15 @@ "microevent.ts": "~0.1.1" } }, + "node_modules/world-calendars": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.4.tgz", + "integrity": "sha512-VGRnLJS+xJmGDPodgJRnGIDwGu0s+Cr9V2HB3EzlDZ5n0qb8h5SJtGUEkjrphZYAglEiXZ6kiXdmk0H/h/uu/w==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.0" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -29555,6 +33652,40 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.3.tgz", + "integrity": "sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==", + "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/src/frontend/static/frontend/package.json b/src/frontend/static/frontend/package.json index 5e1038bb..58a49908 100644 --- a/src/frontend/static/frontend/package.json +++ b/src/frontend/static/frontend/package.json @@ -17,6 +17,11 @@ "author": "", "license": "ISC", "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/preset-env": "^7.27.2", + "@babel/preset-react": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.27.0", @@ -27,18 +32,22 @@ "@types/react-dom": "^18.3.5", "@types/react-virtualized": "^9.22.2", "@types/validator": "^13.12.3", + "babel-loader": "^10.0.0", + "babel-plugin-react-compiler": "^19.1.0-rc.2", "css-loader": "^7.1.2", "eslint": "^9.27.0", "eslint-plugin-jsdoc": "^50.6.9", "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-compiler": "^19.1.0-rc.2", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^16.0.0", "neostandard": "^0.12.1", + "react-compiler-runtime": "^19.1.0-rc.2", "style-loader": "^4.0.0", "ts-checker-rspack-plugin": "^1.1.1", "ts-loader": "^9.5.2", - "typescript": "^5.8.2", - "typescript-eslint": "^8.28.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.59.3", "url-loader": "^4.1.1", "webpack-bundle-tracker": "^3.1.1" }, @@ -50,20 +59,25 @@ "@visx/tooltip": "^3.3.0", "apexcharts": "^3.52.0", "axios": "^1.8.4", - "cytoscape": "^3.30.2", + "cytoscape": "^3.33.1", "d3": "^7.9.0", "dayjs": "^1.11.13", "fomantic-ui-css": "^2.9.4", - "ky": "^1.7.5", + "ky": "2.0.2", "lodash": "^4.17.21", "neo-react-semantic-ui-range": "^0.3.6", + "plotly.js": "^3.3.0", "react": "^18.3.1", "react-apexcharts": "^1.4.1", "react-avatar": "^5.0.3", "react-colorful": "^5.6.1", "react-dom": "^18.3.1", + "react-intl": "^6.8.9", + "react-markdown": "^10.1.0", + "react-plotly.js": "^2.6.0", "react-virtualized": "^9.22.6", "recharts": "^2.15.1", + "remark-gfm": "^4.0.1", "semantic-ui-react": "^2.1.5", "spark-md5": "^3.0.2", "typeface-roboto": "1.1.13", diff --git a/src/frontend/static/frontend/rspack.common.js b/src/frontend/static/frontend/rspack.common.js index 8de93ce3..13bdbbf8 100644 --- a/src/frontend/static/frontend/rspack.common.js +++ b/src/frontend/static/frontend/rspack.common.js @@ -2,6 +2,7 @@ import path, { dirname } from 'path' import { fileURLToPath } from 'url' import BundleTracker from 'webpack-bundle-tracker' import { rspack } from '@rspack/core' +import babelConfig from './babel.config.js' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) @@ -13,6 +14,7 @@ const PATHS = { export const common = { entry: { + differentialExpression: `${PATHS.src}/differential-expression.tsx`, base: `${PATHS.src}/base.tsx`, gem: `${PATHS.src}/gem.tsx`, main: `${PATHS.src}/index.tsx`, @@ -24,7 +26,8 @@ export const common = { survival: `${PATHS.src}/survival.tsx`, aboutUs: `${PATHS.src}/about-us.tsx`, openSource: `${PATHS.src}/open-source.tsx`, - sitePolicy: `${PATHS.src}/site-policy.tsx` + sitePolicy: `${PATHS.src}/site-policy.tsx`, + faq: `${PATHS.src}/faq.tsx` }, output: { path: PATHS.output, @@ -39,13 +42,10 @@ export const common = { { test: /\.ts(x)?$/, exclude: /node_modules/, - loader: 'builtin:swc-loader', - options: { - jsc: { - parser: { - syntax: 'typescript' - } - } + // TODO: Migrate to SWC when React 19 is available. + use:{ + loader: 'babel-loader', + options: babelConfig }, type: 'javascript/auto' }, diff --git a/src/frontend/static/frontend/src/biomarkers.tsx b/src/frontend/static/frontend/src/biomarkers.tsx index 394d4878..5e4bd57d 100644 --- a/src/frontend/static/frontend/src/biomarkers.tsx +++ b/src/frontend/static/frontend/src/biomarkers.tsx @@ -1,10 +1,20 @@ import React from 'react' import { createRoot } from 'react-dom/client' +import './css/biomarkers-form.css' import './css/biomarkers.css' import { BiomarkersPanel } from './components/biomarkers/BiomarkersPanel' +import { IntlProvider } from 'react-intl' +import en from './locales/en' +import es from './locales/es' + +const messages = { en, es } const container = document.getElementById('biomarkers-app') const root = createRoot(container!) -root.render() +root.render( + + + +) diff --git a/src/frontend/static/frontend/src/cgds.tsx b/src/frontend/static/frontend/src/cgds.tsx index 8fec4ed7..fd25c110 100644 --- a/src/frontend/static/frontend/src/cgds.tsx +++ b/src/frontend/static/frontend/src/cgds.tsx @@ -4,6 +4,17 @@ import { createRoot } from 'react-dom/client' import './css/files.css' import { CGDSPanel } from './components/cgds-panel/CGDSPanel' +import { IntlProvider } from 'react-intl' +import en from './locales/en' +import es from './locales/es' + +const messages = { en, es } + const container = document.getElementById('cgds-app') const root = createRoot(container!) -root.render() + +root.render( + + + +) diff --git a/src/frontend/static/frontend/src/components/Base.tsx b/src/frontend/static/frontend/src/components/Base.tsx index b70f80e2..37c5ce36 100644 --- a/src/frontend/static/frontend/src/components/Base.tsx +++ b/src/frontend/static/frontend/src/components/Base.tsx @@ -4,6 +4,27 @@ import ky from 'ky' import { DjangoUser } from '../utils/django_interfaces' import { Nullable } from '../utils/interfaces' import { Footer } from './Footer' +import { IntlProvider } from 'react-intl' +import { ChatWidget } from './assistant/ChatWidget' + +// Locales +import es from '../locales/es' +import en from '../locales/en' + +// Common dependencies for all the pages +import 'fomantic-ui-css/semantic.css' +import '../css/base.css' + +const messages = { en, es } + +interface LocaleContextType { + locale: 'en' | 'es', + setLocale: React.Dispatch> +} +const LocaleContext = React.createContext({ + locale: 'es', + setLocale: () => {} +}) declare const urlCurrentUser: string @@ -29,6 +50,8 @@ const Base = (props: BaseProps) => { const abortController = useRef(new AbortController()) const [currentUser, setUser] = useState>(null) const [isLoadingCurrentUser, setIsLoadingCurrentUser] = useState(true) + // State that defines the current language ('es' or 'en') for , used to display the interface in the selected locale + const [locale, setLocale] = useState<'en' | 'es'>('es') /** * Method which is executed when the component has mounted @@ -47,7 +70,7 @@ const Base = (props: BaseProps) => { */ function getCurrentUser () { ky.get(urlCurrentUser, { retry: 5, signal: abortController.current.signal }).then((response) => { - response.json().then((currentUser: DjangoUser) => { + response.json().then((currentUser) => { setUser(currentUser) }).catch((err) => { console.log('Error parsing JSON ->', err) @@ -75,17 +98,24 @@ const Base = (props: BaseProps) => { return ( - {/* Navbar */} - + + + {/* Navbar */} + + + {/* Composition part */} +
+ {props.children} +
- {/* Composition part */} -
- {props.children} -
+ {/* Footer */} + {/* TODO: add license */} +