diff --git a/_bmad-output/architecture-backend.md b/_bmad-output/architecture-backend.md
index 6e87b5263..d2c64f175 100644
--- a/_bmad-output/architecture-backend.md
+++ b/_bmad-output/architecture-backend.md
@@ -38,3 +38,75 @@ Backend của SurfSense là một ứng dụng **Python FastAPI** mạnh mẽ,
- Nếu là tác vụ AI: Đẩy vào LangGraph runner để streaming phản hồi.
- Nếu là tác vụ dài (Ingestion): Đẩy job vào Redis queue cho Celery.
5. **Response**: Trả về JSON hoặc Streaming Response (SSE).
+
+## Critical RAG Pipeline Fix (Feb 2026)
+
+### DexScreener Connector Integration
+
+**Issue Discovered**: DexScreener connector was successfully implemented and indexed data into `search_space_id = 7`, but the LLM could not retrieve this data when users asked about crypto prices.
+
+**Root Cause**: Missing connector mapping in `_CONNECTOR_TYPE_TO_SEARCHABLE` dictionary.
+
+**File**: `surfsense_backend/app/agents/new_chat/chat_deepagent.py`
+
+**The Problem**:
+```python
+# BEFORE (Missing mapping)
+_CONNECTOR_TYPE_TO_SEARCHABLE = {
+ "GMAIL": "GMAIL",
+ "GOOGLE_DRIVE_CONNECTOR": "GOOGLE_DRIVE",
+ "SLACK_CONNECTOR": "SLACK",
+ # ... other connectors ...
+ # ❌ DEXSCREENER_CONNECTOR was MISSING
+}
+```
+
+**Impact**:
+1. `connector_service.get_available_connectors()` returned DexScreener connector type
+2. `_map_connectors_to_searchable_types()` could not find mapping → ignored DexScreener
+3. LLM's tool description didn't mention DexScreener as available
+4. LLM never searched DexScreener data, responded "can't see price data"
+
+**The Fix**:
+```python
+# AFTER (Fixed)
+_CONNECTOR_TYPE_TO_SEARCHABLE = {
+ "GMAIL": "GMAIL",
+ "GOOGLE_DRIVE_CONNECTOR": "GOOGLE_DRIVE",
+ "SLACK_CONNECTOR": "SLACK",
+ # ... other connectors ...
+ "DEXSCREENER_CONNECTOR": "DEXSCREENER_CONNECTOR", # ✅ Added
+}
+```
+
+**Verification**:
+- User query: *"What's the current price of WETH?"*
+- LLM successfully retrieved: ~$2,442 USD with DexScreener citations
+- Citations linked to indexed trading pairs with metadata (chain, DEX, liquidity, volume)
+
+**Lesson Learned**: When adding new connectors, **ALWAYS** update the `_CONNECTOR_TYPE_TO_SEARCHABLE` mapping to enable RAG retrieval. This is a critical step that's easy to miss during implementation.
+
+---
+
+## Connector Architecture Pattern
+
+### Adding New Connectors (Best Practices)
+
+Khi thêm connector mới, cần update **4 locations**:
+
+1. **Connector Class** (`app/connectors/`)
+ - Implement data fetching logic
+ - Format data to markdown for indexing
+
+2. **Database Enum** (`app/db.py`)
+ - Add to `SearchSourceConnectorType` enum
+
+3. **API Routes** (`app/routes/`)
+ - Create add/delete/test endpoints
+
+4. **RAG Mapping** (`app/agents/new_chat/chat_deepagent.py`) ⚠️ **CRITICAL**
+ - Add to `_CONNECTOR_TYPE_TO_SEARCHABLE` dictionary
+ - **Failure to do this = LLM cannot access connector data**
+
+---
+
diff --git a/_bmad-output/connectors-explained.md b/_bmad-output/connectors-explained.md
index 9b68af41d..a9b0bfaa8 100644
--- a/_bmad-output/connectors-explained.md
+++ b/_bmad-output/connectors-explained.md
@@ -18,8 +18,9 @@
- 🎫 **Jira** - Tìm kiếm tickets
- 📚 **Confluence** - Tìm kiếm wiki pages
- 🗂️ **Microsoft Teams** - Tìm kiếm chats và files
+- 💰 **DexScreener** - Theo dõi giá token crypto và trading pairs
-**Tổng cộng:** SurfSense hỗ trợ **26+ connectors** khác nhau!
+**Tổng cộng:** SurfSense hỗ trợ **27+ connectors** khác nhau!
---
@@ -238,6 +239,30 @@ User → SurfSense → Notion OAuth → Access Token → SurfSense
- Chỉ hoạt động khi SurfSense chạy self-hosted
- Truy cập trực tiếp vào local file system
+### 5. API-Based (No Authentication)
+
+**Ví dụ:** DexScreener Connector
+
+- Không cần OAuth hay API key (public API)
+- User chỉ cần cấu hình tokens muốn theo dõi
+- Ưu điểm:
+ - Setup cực kỳ đơn giản (không cần đăng ký API key)
+ - Miễn phí hoàn toàn
+ - Real-time data từ public blockchain
+- Nhược điểm:
+ - Bị giới hạn rate limit của public API
+ - Không có personalized data
+
+**Flow:**
+```
+User → Nhập token addresses → SurfSense → DexScreener Public API → Token Price Data
+```
+
+**Use Case:**
+- Theo dõi giá crypto tokens (WETH, USDC, etc.)
+- Phân tích trading pairs trên các DEX
+- AI có thể trả lời: *"What's the current price of WETH?"*
+
---
## 🛠️ Cấu Hình Connector
@@ -278,6 +303,24 @@ Mỗi connector có các settings:
}
```
+**DexScreener:**
+```json
+{
+ "tokens": [
+ {
+ "chain": "ethereum",
+ "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
+ "name": "WETH"
+ },
+ {
+ "chain": "bsc",
+ "address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
+ "name": "WBNB"
+ }
+ ]
+}
+```
+
---
## 💡 Use Cases
@@ -320,6 +363,23 @@ Mỗi connector có các settings:
3. Slack discussion về issue
4. Confluence: Payment API Architecture
+### 3. Crypto Trader
+
+**Scenario:** Theo dõi giá token và phân tích market trends.
+
+**Connectors kết nối:**
+- DexScreener (token prices và trading pairs)
+- Twitter/X (crypto news - nếu có connector)
+- Notion (trading notes)
+
+**Search query trong AI Chat:** *"What's the current price of WETH and how has it changed in the last 24 hours?"*
+
+**Kết quả:**
+- AI trả lời với real-time price data từ DexScreener
+- Hiển thị price changes (5m, 1h, 24h)
+- Liquidity và volume information
+- Citations link đến DexScreener pairs
+
---
## 🚨 Lưu Ý Quan Trọng
diff --git a/_bmad-output/custom-connector-guide.md b/_bmad-output/custom-connector-guide.md
index 7620a486b..813213e80 100644
--- a/_bmad-output/custom-connector-guide.md
+++ b/_bmad-output/custom-connector-guide.md
@@ -796,6 +796,76 @@ async def index_connector_data(connector: SearchSourceConnector):
# ... other connector types
```
+---
+
+## ⚠️ CRITICAL: Enable RAG Retrieval
+
+**This is the most commonly missed step when adding new connectors!**
+
+### The Problem
+
+Even if your connector successfully:
+1. ✅ Stores data in the database
+2. ✅ Indexes data into vector store
+3. ✅ Shows up in the UI
+
+The LLM **WILL NOT** be able to retrieve this data unless you add the connector to the RAG mapping.
+
+### The Fix
+
+**File:** `surfsense_backend/app/agents/new_chat/chat_deepagent.py`
+
+**Add your connector to `_CONNECTOR_TYPE_TO_SEARCHABLE`:**
+
+```python
+_CONNECTOR_TYPE_TO_SEARCHABLE = {
+ "GMAIL": "GMAIL",
+ "GOOGLE_DRIVE_CONNECTOR": "GOOGLE_DRIVE",
+ "SLACK_CONNECTOR": "SLACK",
+ "NOTION_CONNECTOR": "NOTION",
+ # ... other connectors ...
+
+ # ✅ ADD YOUR NEW CONNECTOR HERE
+ "DEXSCREENER_CONNECTOR": "DEXSCREENER_CONNECTOR",
+ "YOUR_CONNECTOR": "YOUR_CONNECTOR", # Example
+}
+```
+
+### Why This Matters
+
+This mapping is used by `_map_connectors_to_searchable_types()` to:
+1. Build the list of available search spaces for the LLM
+2. Include connector types in the tool description
+3. Enable the LLM to search your connector's data
+
+**Without this mapping:**
+- LLM won't know your connector exists
+- LLM can't search your indexed data
+- Users will get "I don't have access to that data" responses
+
+### Verification
+
+After adding the mapping, test with a user query:
+
+```bash
+# Example for DexScreener
+curl -X POST http://localhost:8000/api/chat \
+ -H "Authorization: Bearer $TOKEN" \
+ -d '{
+ "message": "What is the current price of WETH?",
+ "space_id": 7
+ }'
+```
+
+**Expected:** LLM retrieves data and provides answer with citations.
+
+**If failed:** Check that:
+1. Connector is in `_CONNECTOR_TYPE_TO_SEARCHABLE`
+2. Connector type matches exactly (case-sensitive)
+3. Data is indexed in the correct `search_space_id`
+
+---
+
## Best Practices
### 1. **Error Handling**
diff --git a/_bmad-output/user-guide.md b/_bmad-output/user-guide.md
index e77237fd5..ddf94c403 100644
--- a/_bmad-output/user-guide.md
+++ b/_bmad-output/user-guide.md
@@ -195,6 +195,11 @@ SurfSense cho phép bạn kết nối với **26+ ứng dụng bên ngoài** nh
- **Kết nối Google Drive:** Tìm files và documents ngay trong SurfSense
- **Kết nối Slack:** Tìm conversations và shared files từ workspace
- **Kết nối Notion:** Tìm kiếm trong pages và databases
+- **Kết nối DexScreener:** Theo dõi giá crypto tokens real-time
+ - Không cần API key
+ - Chỉ cần nhập token addresses muốn theo dõi
+ - AI có thể trả lời: *"What's the current price of WETH?"*
+ - Xem trading pairs, liquidity, volume, price changes
**Quản lý Connectors:**
diff --git a/_bmad-stories/story-1.1-dexscreener-connector.md b/_bmad-stories/story-1.1-dexscreener-connector.md
index 09b726323..605f86226 100644
--- a/_bmad-stories/story-1.1-dexscreener-connector.md
+++ b/_bmad-stories/story-1.1-dexscreener-connector.md
@@ -6,7 +6,7 @@
**Story Title**: DexScreener Connector Integration
**Epic**: SurfSense Connectors Enhancement
**Priority**: High
-**Status**: Ready for Development
+**Status**: ✅ Implementation Complete (2026-02-01)
**Created**: 2026-01-31
## 🎯 User Story
@@ -27,63 +27,63 @@ This connector will integrate with SurfSense's existing connector architecture,
## ✅ Acceptance Criteria
-### AC1: Connector Configuration
-- [ ] User can add DexScreener connector via API endpoint
-- [ ] User can configure multiple tokens to track (up to 50)
-- [ ] Each token config includes: chain ID, token address, optional name
-- [ ] User can update connector configuration
-- [ ] User can delete connector
-- [ ] Configuration is persisted in database
+### AC1: Connector Configuration ✅
+- [x] User can add DexScreener connector via API endpoint
+- [x] User can configure multiple tokens to track (up to 50)
+- [x] Each token config includes: chain ID, token address, optional name
+- [x] User can update connector configuration
+- [x] User can delete connector
+- [x] Configuration is persisted in database
-### AC2: API Integration
-- [ ] Connector successfully calls DexScreener API endpoints
-- [ ] Handles rate limits (300 req/min) appropriately
-- [ ] Implements retry logic with exponential backoff
-- [ ] Validates API responses
-- [ ] Handles API errors gracefully (network failures, invalid data, etc.)
+### AC2: API Integration ✅
+- [x] Connector successfully calls DexScreener API endpoints
+- [x] Handles rate limits (300 req/min) appropriately
+- [x] Implements retry logic with exponential backoff
+- [x] Validates API responses
+- [x] Handles API errors gracefully (network failures, invalid data, etc.)
-### AC3: Data Indexing
-- [ ] Fetches trading pairs for all configured tokens
-- [ ] Converts pair data to markdown format with all key metrics:
+### AC3: Data Indexing ✅
+- [x] Fetches trading pairs for all configured tokens
+- [x] Converts pair data to markdown format with all key metrics:
- Token information (names, symbols, addresses)
- Price data (USD, native, 24h changes)
- Volume metrics (24h, 6h, 1h)
- Liquidity information
- Market cap and FDV
- Transaction counts
-- [ ] Generates unique identifier hash for each pair
-- [ ] Generates content hash to detect changes
-- [ ] Creates document chunks for vector search
-- [ ] Generates embeddings using configured LLM
-- [ ] Stores documents in database with proper metadata
-- [ ] Updates existing documents when content changes
-- [ ] Skips unchanged documents
+- [x] Generates unique identifier hash for each pair
+- [x] Generates content hash to detect changes
+- [x] Creates document chunks for vector search
+- [x] Generates embeddings using configured LLM
+- [x] Stores documents in database with proper metadata
+- [x] Updates existing documents when content changes
+- [x] Skips unchanged documents
-### AC4: Periodic Indexing
-- [ ] Indexing task is registered with Celery
-- [ ] Periodic scheduler triggers indexing (default: 60 min interval)
-- [ ] Manual indexing can be triggered via API
-- [ ] Last indexed timestamp is updated after successful indexing
-- [ ] Indexing errors are logged properly
-- [ ] Failed indexing doesn't block future attempts
+### AC4: Periodic Indexing ✅
+- [x] Indexing task is registered with Celery
+- [x] Periodic scheduler triggers indexing (default: 60 min interval)
+- [x] Manual indexing can be triggered via API
+- [x] Last indexed timestamp is updated after successful indexing
+- [x] Indexing errors are logged properly
+- [x] Failed indexing doesn't block future attempts
-### AC5: Search Integration
-- [ ] Indexed DexScreener data appears in search results
-- [ ] Documents are searchable by:
+### AC5: Search Integration ✅
+- [x] Indexed DexScreener data appears in search results
+- [x] Documents are searchable by:
- Token names and symbols
- Pair addresses
- Chain IDs
- DEX names
- Price ranges
- Volume metrics
-- [ ] Search results include relevant metadata
-- [ ] Vector search returns semantically similar pairs
+- [x] Search results include relevant metadata
+- [x] Vector search returns semantically similar pairs
-### AC6: AI Chat Integration
-- [ ] AI chat can access DexScreener data as context
-- [ ] Chat responses include relevant trading pair information
-- [ ] Citations link to DexScreener URLs
-- [ ] Metadata is properly formatted in chat responses
+### AC6: AI Chat Integration ✅
+- [x] AI chat can access DexScreener data as context
+- [x] Chat responses include relevant trading pair information
+- [x] Citations link to DexScreener URLs
+- [x] Metadata is properly formatted in chat responses
## 🏗️ Technical Implementation
diff --git a/_bmad-stories/story-1.2-dexscreener-frontend.md b/_bmad-stories/story-1.2-dexscreener-frontend.md
index a27af7662..8d8029de6 100644
--- a/_bmad-stories/story-1.2-dexscreener-frontend.md
+++ b/_bmad-stories/story-1.2-dexscreener-frontend.md
@@ -6,7 +6,7 @@
**Story Title**: DexScreener Connector Frontend UI
**Epic**: SurfSense Connectors Enhancement
**Priority**: High
-**Status**: Ready for Development
+**Status**: ✅ Implementation Complete (2026-02-01)
**Created**: 2026-01-31
**Depends On**: Story 1.1 (Backend API)
@@ -29,21 +29,21 @@ This story implements the user-facing components following SurfSense's establish
## ✅ Acceptance Criteria
-### AC1: Connect Form Component
-- [ ] User can access DexScreener connector from connector popup
-- [ ] Form includes connector name field (min 3 characters)
-- [ ] User can add multiple tokens (up to 50)
-- [ ] Each token has: chain selector, token address input, optional name
-- [ ] Form validates token addresses (40-character hex)
-- [ ] User can remove tokens from list
-- [ ] Date range selector for initial indexing
-- [ ] Periodic sync toggle with frequency selector
-- [ ] "What you get" benefits section displayed
-- [ ] Form submits to backend API endpoint
+### AC1: Connect Form Component ✅
+- [x] User can access DexScreener connector from connector popup
+- [x] Form includes connector name field (min 3 characters)
+- [x] User can add multiple tokens (up to 50)
+- [x] Each token has: chain selector, token address input, optional name
+- [x] Form validates token addresses (40-character hex)
+- [x] User can remove tokens from list
+- [x] Date range selector for initial indexing
+- [x] Periodic sync toggle with frequency selector
+- [x] "What you get" benefits section displayed
+- [x] Form submits to backend API endpoint
-### AC2: Token Management UI
-- [ ] Dynamic token list with add/remove buttons
-- [ ] Chain selector dropdown with popular chains:
+### AC2: Token Management UI ✅
+- [x] Dynamic token list with add/remove buttons
+- [x] Chain selector dropdown with popular chains:
- Ethereum
- BSC (Binance Smart Chain)
- Polygon
@@ -52,42 +52,42 @@ This story implements the user-facing components following SurfSense's establish
- Base
- Avalanche
- Solana
-- [ ] Token address input with validation
-- [ ] Optional token name/label field
-- [ ] Visual feedback for validation errors
-- [ ] Responsive design for mobile/desktop
+- [x] Token address input with validation
+- [x] Optional token name/label field
+- [x] Visual feedback for validation errors
+- [x] Responsive design for mobile/desktop
-### AC3: Connector Config Component
-- [ ] Edit mode for existing connector
-- [ ] Update connector name
-- [ ] Add/remove tokens from tracked list
-- [ ] View current token configuration
-- [ ] Save changes button
-- [ ] Cancel/discard changes option
+### AC3: Connector Config Component ✅
+- [x] Edit mode for existing connector
+- [x] Update connector name
+- [x] Add/remove tokens from tracked list
+- [x] View current token configuration
+- [x] Save changes button
+- [x] Cancel/discard changes option
-### AC4: Connector Benefits
-- [ ] Display benefits list in connect form
-- [ ] Benefits include:
+### AC4: Connector Benefits ✅
+- [x] Display benefits list in connect form
+- [x] Benefits include:
- "Real-time cryptocurrency trading pair data"
- "Track prices, volume, and liquidity across multiple DEXs"
- "Search and analyze token market data with AI"
- "Monitor your crypto portfolio with automated updates"
- "Access historical price and volume trends"
-### AC5: Documentation
-- [ ] MDX documentation file created
-- [ ] Setup guide with screenshots
-- [ ] Token configuration instructions
-- [ ] Chain selection guide
-- [ ] Troubleshooting section
-- [ ] Link to DexScreener API docs
+### AC5: Documentation ✅
+- [x] MDX documentation file created
+- [x] Setup guide with screenshots
+- [x] Token configuration instructions
+- [x] Chain selection guide
+- [x] Troubleshooting section
+- [x] Link to DexScreener API docs
-### AC6: Integration
-- [ ] Connector registered in connector registry
-- [ ] Icon/logo added to public assets
-- [ ] Connector appears in connector list
-- [ ] Form properly integrated with connector popup
-- [ ] Config component properly integrated
+### AC6: Integration ✅
+- [x] Connector registered in connector registry
+- [x] Icon/logo added to public assets
+- [x] Connector appears in connector list
+- [x] Form properly integrated with connector popup
+- [x] Config component properly integrated
## 🏗️ Technical Implementation
diff --git a/surfsense_web/__tests__/components/assistant-ui/connector-popup/connect-forms/components/dexscreener-connect-form.test.tsx b/surfsense_web/__tests__/components/assistant-ui/connector-popup/connect-forms/components/dexscreener-connect-form.test.tsx
new file mode 100644
index 000000000..bf8e962d3
--- /dev/null
+++ b/surfsense_web/__tests__/components/assistant-ui/connector-popup/connect-forms/components/dexscreener-connect-form.test.tsx
@@ -0,0 +1,204 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { DexScreenerConnectForm } from '@/components/assistant-ui/connector-popup/connect-forms/components/dexscreener-connect-form';
+
+// Mock the form submission
+const mockOnSubmit = vi.fn();
+const mockOnBack = vi.fn();
+
+describe('DexScreenerConnectForm', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe('Initial Rendering', () => {
+ it('should render the form with all required fields', () => {
+ render();
+
+ // Check for connector name input
+ expect(screen.getByLabelText('Connector Name')).toBeInTheDocument();
+
+ // Check for benefits section
+ expect(screen.getByText('No API Key Required')).toBeInTheDocument();
+
+ // Check for add token button
+ expect(screen.getByRole('button', { name: /add token/i })).toBeInTheDocument();
+ });
+
+ it('should display the DexScreener info alert', () => {
+ render();
+
+ expect(screen.getByText('No API Key Required')).toBeInTheDocument();
+ expect(screen.getByText(/DexScreener API is public and free to use/i)).toBeInTheDocument();
+ });
+ });
+
+ describe('Form Validation', () => {
+ it('should accept valid connector name', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const nameInput = screen.getByLabelText('Connector Name');
+ await user.clear(nameInput);
+ await user.type(nameInput, 'Valid Name');
+
+ // Should accept valid name
+ expect(nameInput).toHaveValue('Valid Name');
+ });
+
+ it('should accept valid Ethereum address format', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const addressInput = screen.getByLabelText('Token Address');
+ const validAddress = '0x' + 'a'.repeat(40);
+ await user.clear(addressInput);
+ await user.type(addressInput, validAddress);
+
+ // Should accept valid address
+ expect(addressInput).toHaveValue(validAddress);
+ });
+ });
+
+ describe('Token Management', () => {
+ it('should add a new token when Add Token button is clicked', async () => {
+ const user = userEvent.setup();
+ render();
+
+ // Initially should have 1 token (default)
+ expect(screen.getByText('Token #1')).toBeInTheDocument();
+
+ const addTokenButton = screen.getByRole('button', { name: /add token/i });
+ await user.click(addTokenButton);
+
+ // Should now have 2 tokens
+ await waitFor(() => {
+ expect(screen.getByText('Token #2')).toBeInTheDocument();
+ });
+ });
+
+ it('should remove a token when remove button is clicked', async () => {
+ const user = userEvent.setup();
+ render();
+
+ // Add a second token first
+ const addTokenButton = screen.getByRole('button', { name: /add token/i });
+ await user.click(addTokenButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Token #2')).toBeInTheDocument();
+ });
+
+ // Remove the second token
+ const removeButtons = screen.getAllByRole('button', { name: '' }); // X buttons have no text
+ const lastRemoveButton = removeButtons[removeButtons.length - 1];
+ await user.click(lastRemoveButton);
+
+ // Token #2 should be gone
+ await waitFor(() => {
+ expect(screen.queryByText('Token #2')).not.toBeInTheDocument();
+ });
+ });
+
+ it('should allow adding multiple tokens up to the limit', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const addTokenButton = screen.getByRole('button', { name: /add token/i });
+
+ // Add 2 more tokens (already have 1)
+ await user.click(addTokenButton);
+ await user.click(addTokenButton);
+
+ // Should have 3 tokens total
+ await waitFor(() => {
+ expect(screen.getByText('Token #3')).toBeInTheDocument();
+ expect(screen.getByText('3 / 50 tokens')).toBeInTheDocument();
+ });
+ });
+
+ it('should disable Add Token button when maximum tokens (50) are reached', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const addTokenButton = screen.getByRole('button', { name: /add token/i });
+
+ // Add 49 more tokens (already have 1) - this is slow but necessary
+ for (let i = 0; i < 49; i++) {
+ await user.click(addTokenButton);
+ }
+
+ // Button should be disabled and show max message
+ await waitFor(() => {
+ expect(addTokenButton).toBeDisabled();
+ expect(screen.getByText(/maximum reached/i)).toBeInTheDocument();
+ }, { timeout: 10000 });
+ });
+ });
+
+ describe('Chain Selection', () => {
+ it('should display supported chains in the dropdown', async () => {
+ const user = userEvent.setup();
+ render();
+
+ // Click on the chain selector
+ const chainSelect = screen.getByRole('combobox', { name: /chain/i });
+ await user.click(chainSelect);
+
+ // Check for supported chains
+ await waitFor(() => {
+ expect(screen.getByRole('option', { name: /ethereum/i })).toBeInTheDocument();
+ expect(screen.getByRole('option', { name: /bsc/i })).toBeInTheDocument();
+ expect(screen.getByRole('option', { name: /polygon/i })).toBeInTheDocument();
+ });
+ });
+
+ it('should allow chain selection', async () => {
+ const user = userEvent.setup();
+ render();
+
+ const chainSelect = screen.getByRole('combobox', { name: /chain/i });
+ await user.click(chainSelect);
+
+ // Select BSC
+ const bscOption = screen.getByRole('option', { name: /bsc/i });
+ await user.click(bscOption);
+
+ // Verify dropdown closed (option should not be visible anymore)
+ await waitFor(() => {
+ expect(screen.queryByRole('option', { name: /bsc/i })).not.toBeInTheDocument();
+ });
+ });
+ });
+
+ describe('Form Submission', () => {
+ it('should call onSubmit with valid data', async () => {
+ const user = userEvent.setup();
+ render();
+
+ // Fill connector name
+ const nameInput = screen.getByLabelText('Connector Name');
+ await user.clear(nameInput);
+ await user.type(nameInput, 'My Connector');
+
+ // Fill token address (first token already exists)
+ const addressInput = screen.getByLabelText('Token Address');
+ const validAddress = '0x' + 'a'.repeat(40);
+ await user.type(addressInput, validAddress);
+
+ // Find and click submit button
+ const buttons = screen.getAllByRole('button');
+ const submitButton = buttons.find(btn => btn.textContent?.includes('Connect'));
+
+ if (submitButton) {
+ await user.click(submitButton);
+
+ // Verify onSubmit was called
+ await waitFor(() => {
+ expect(mockOnSubmit).toHaveBeenCalled();
+ }, { timeout: 3000 });
+ }
+ });
+ });
+});
diff --git a/surfsense_web/package.json b/surfsense_web/package.json
index 52bd9a5a2..1d5c0facd 100644
--- a/surfsense_web/package.json
+++ b/surfsense_web/package.json
@@ -18,7 +18,10 @@
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
- "format:fix": "npx @biomejs/biome check --fix"
+ "format:fix": "npx @biomejs/biome check --fix",
+ "test": "vitest",
+ "test:ui": "vitest --ui",
+ "test:coverage": "vitest --coverage"
},
"dependencies": {
"@ai-sdk/react": "^1.2.12",
@@ -114,18 +117,25 @@
"@eslint/eslintrc": "^3.3.1",
"@tailwindcss/postcss": "^4.1.11",
"@tailwindcss/typography": "^0.5.16",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@testing-library/user-event": "^14.6.1",
"@types/canvas-confetti": "^1.9.0",
"@types/node": "^20.19.9",
"@types/pg": "^8.15.5",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
+ "@vitejs/plugin-react": "^4.7.0",
+ "@vitest/ui": "^3.2.4",
"cross-env": "^7.0.3",
"drizzle-kit": "^0.31.5",
"eslint": "^9.32.0",
"eslint-config-next": "15.2.0",
+ "jsdom": "^25.0.1",
"tailwindcss": "^4.1.11",
"tsx": "^4.20.6",
"typescript": "^5.8.3",
- "vite": "^7.3.1"
+ "vite": "^7.3.1",
+ "vitest": "^3.2.4"
}
-}
+}
\ No newline at end of file
diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml
index 40c80964b..1a01c5a21 100644
--- a/surfsense_web/pnpm-lock.yaml
+++ b/surfsense_web/pnpm-lock.yaml
@@ -166,22 +166,22 @@ importers:
version: 1.4.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
fumadocs-core:
specifier: ^16.3.1
- version: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
+ version: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
fumadocs-mdx:
specifier: ^14.2.1
- version: 14.2.1(fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
+ version: 14.2.1(fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
fumadocs-ui:
specifier: ^16.3.1
- version: 16.3.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)
+ version: 16.3.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)
geist:
specifier: ^1.4.2
- version: 1.5.1(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
+ version: 1.5.1(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
jotai:
specifier: ^2.15.1
- version: 2.16.0(@types/react@19.2.7)(react@19.2.3)
+ version: 2.16.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.7)(react@19.2.3)
jotai-tanstack-query:
specifier: ^0.11.0
- version: 0.11.0(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(jotai@2.16.0(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
+ version: 0.11.0(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(jotai@2.16.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3)
lucide-react:
specifier: ^0.477.0
version: 0.477.0(react@19.2.3)
@@ -190,10 +190,10 @@ importers:
version: 12.23.26(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next:
specifier: ^16.1.0
- version: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ version: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-intl:
specifier: ^4.6.1
- version: 4.6.1(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
+ version: 4.6.1(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -282,6 +282,15 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.19(tailwindcss@4.1.18)
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.1)
'@types/canvas-confetti':
specifier: ^1.9.0
version: 1.9.0
@@ -297,6 +306,12 @@ importers:
'@types/react-dom':
specifier: ^19.1.6
version: 19.2.3(@types/react@19.2.7)
+ '@vitejs/plugin-react':
+ specifier: ^4.7.0
+ version: 4.7.0(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
+ '@vitest/ui':
+ specifier: ^3.2.4
+ version: 3.2.4(vitest@3.2.4)
cross-env:
specifier: ^7.0.3
version: 7.0.3
@@ -309,6 +324,9 @@ importers:
eslint-config-next:
specifier: 15.2.0
version: 15.2.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
+ jsdom:
+ specifier: ^25.0.1
+ version: 25.0.1
tailwindcss:
specifier: ^4.1.11
version: 4.1.18
@@ -321,9 +339,15 @@ importers:
vite:
specifier: ^7.3.1
version: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ vitest:
+ specifier: ^3.2.4
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(tsx@4.21.0)
packages:
+ '@adobe/css-tools@4.4.4':
+ resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+
'@ai-sdk/gateway@2.0.23':
resolution: {integrity: sha512-qmX7afPRszUqG5hryHF3UN8ITPIRSGmDW6VYCmByzjoUkgm3MekzSx2hMV1wr0P+llDeuXb378SjqUfpvWJulg==}
engines: {node: '>=18'}
@@ -433,10 +457,93 @@ packages:
react:
optional: true
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.0':
+ resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.0':
+ resolution: {integrity: sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.0':
+ resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/runtime@7.28.4':
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'}
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
'@biomejs/biome@2.1.2':
resolution: {integrity: sha512-yq8ZZuKuBVDgAS76LWCfFKHSYIAgqkxVB3mGVVpOe2vSkUTs7xG46zXZeNPRNVjiJuw0SZ3+J2rXiYx0RUpfGg==}
engines: {node: '>=14.21.3'}
@@ -1605,6 +1712,9 @@ packages:
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
engines: {node: '>= 10.0.0'}
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
'@posthog/core@1.15.0':
resolution: {integrity: sha512-n2/Yy0+qc8xhmlcOFiYqTcGHBZuuaQjVolfFXk7yTCynzdMe8Fx1zYvPPUrbdQK5tWwXyilkzybpqhK6I7aV4Q==}
@@ -2503,6 +2613,9 @@ packages:
'@remirror/core-constants@3.0.0':
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
+ '@rolldown/pluginutils@1.0.0-beta.27':
+ resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
'@rollup/rollup-android-arm-eabi@4.55.1':
resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==}
cpu: [arm]
@@ -2886,6 +2999,35 @@ packages:
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.2':
+ resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
'@tiptap/core@3.14.0':
resolution: {integrity: sha512-nm0VWVA1Vq/jaKY3wyRXViL/kf78yMdH7qETpv4qZXDQLU+pdWV3IGoRTQTKESc7d8L1wL/2uCeByLNUJfrSIw==}
peerDependencies:
@@ -2978,9 +3120,27 @@ packages:
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
'@types/canvas-confetti@1.9.0':
resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==}
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
'@types/d3-array@3.2.2':
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
@@ -3077,6 +3237,9 @@ packages:
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
'@types/diff-match-patch@1.0.36':
resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
@@ -3315,6 +3478,46 @@ packages:
resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==}
engines: {node: '>= 20'}
+ '@vitejs/plugin-react@4.7.0':
+ resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ '@vitest/expect@3.2.4':
+ resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
+
+ '@vitest/mocker@3.2.4':
+ resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@3.2.4':
+ resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
+
+ '@vitest/runner@3.2.4':
+ resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
+
+ '@vitest/snapshot@3.2.4':
+ resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
+
+ '@vitest/spy@3.2.4':
+ resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
+
+ '@vitest/ui@3.2.4':
+ resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==}
+ peerDependencies:
+ vitest: 3.2.4
+
+ '@vitest/utils@3.2.4':
+ resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -3348,10 +3551,18 @@ packages:
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -3359,6 +3570,9 @@ packages:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
aria-query@5.3.2:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
@@ -3395,6 +3609,10 @@ packages:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
assistant-cloud@0.1.12:
resolution: {integrity: sha512-A2tY6QIdP9+RkE8Mmpm4kAoO0NyKsKpJKYebbYFZ3bAnQKyB15Bw/PS9AovpdeziGU9At97TyiMrT36pDjCD7A==}
@@ -3463,12 +3681,21 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
@@ -3494,6 +3721,10 @@ packages:
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ chai@5.3.3:
+ resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
+ engines: {node: '>=18'}
+
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -3523,6 +3754,10 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+ check-error@2.1.3:
+ resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==}
+ engines: {node: '>= 16'}
+
chevrotain-allstar@0.3.1:
resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
peerDependencies:
@@ -3600,6 +3835,9 @@ packages:
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
core-js@3.47.0:
resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==}
@@ -3621,6 +3859,9 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -3844,6 +4085,10 @@ packages:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
@@ -3892,6 +4137,12 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dompurify@3.3.1:
resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
@@ -3999,6 +4250,9 @@ packages:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ electron-to-chromium@1.5.283:
+ resolution: {integrity: sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==}
+
emblor@1.4.8:
resolution: {integrity: sha512-Vqtz4Gepa7CIkmplQ+kvJnsSZJ4sAyHvQqqX2iCmgoRo5iRQFxr+5FJkk6QuLVNH5vrbBZEYxg7sMZuDCnQ/PQ==}
peerDependencies:
@@ -4042,6 +4296,9 @@ packages:
resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==}
engines: {node: '>= 0.4'}
+ es-module-lexer@1.7.0:
+ resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
+
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -4084,6 +4341,10 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
@@ -4243,6 +4504,10 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
+ expect-type@1.3.0:
+ resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
+ engines: {node: '>=12.0.0'}
+
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@@ -4281,6 +4546,9 @@ packages:
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+ fflate@0.8.2:
+ resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -4433,6 +4701,10 @@ packages:
resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==}
engines: {node: '>= 0.4'}
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
get-east-asian-width@1.4.0:
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
engines: {node: '>=18'}
@@ -4653,6 +4925,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -4861,6 +5137,9 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
@@ -4874,6 +5153,11 @@ packages:
canvas:
optional: true
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -4890,6 +5174,11 @@ packages:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
jsondiffpatch@0.6.0:
resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -5034,12 +5323,18 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
+ loupe@3.2.1:
+ resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
+
lowlight@1.20.0:
resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
lucide-react@0.477.0:
resolution: {integrity: sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw==}
peerDependencies:
@@ -5050,6 +5345,10 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -5287,6 +5586,10 @@ packages:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -5323,6 +5626,10 @@ packages:
react-dom:
optional: true
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -5398,6 +5705,9 @@ packages:
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
npm-to-yarn@3.0.1:
resolution: {integrity: sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5504,6 +5814,10 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+ pathval@2.0.1:
+ resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
+ engines: {node: '>= 14.16'}
+
pg-cloudflare@1.2.7:
resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
@@ -5620,6 +5934,10 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
prismjs@1.27.0:
resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==}
engines: {node: '>=6'}
@@ -5793,6 +6111,9 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react-json-view-lite@2.5.0:
resolution: {integrity: sha512-tk7o7QG9oYyELWHL8xiMQ8x4WzjCzbWNyig3uexmkLb54r8jO0yH3WCWx8UZS0c49eSA4QUmG5caiRJ8fAn58g==}
engines: {node: '>=18'}
@@ -5817,6 +6138,10 @@ packages:
react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@@ -5909,6 +6234,10 @@ packages:
recma-stringify@1.0.0:
resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -6138,12 +6467,19 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
sonner@2.0.7:
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
peerDependencies:
@@ -6178,6 +6514,12 @@ packages:
stable-hash@0.0.5:
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.10.0:
+ resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
+
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@@ -6220,6 +6562,10 @@ packages:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
@@ -6228,6 +6574,9 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ strip-literal@3.1.0:
+ resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
+
style-to-js@1.1.21:
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
@@ -6295,6 +6644,12 @@ packages:
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
engines: {node: '>=18'}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
tinyexec@1.0.2:
resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
engines: {node: '>=18'}
@@ -6303,6 +6658,18 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinypool@1.1.1:
+ resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@4.0.4:
+ resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
+ engines: {node: '>=14.0.0'}
+
tldts-core@6.1.86:
resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==}
@@ -6314,6 +6681,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
@@ -6427,6 +6798,12 @@ packages:
unrs-resolver@1.11.1:
resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -6513,6 +6890,11 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ vite-node@3.2.4:
+ resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -6553,6 +6935,34 @@ packages:
yaml:
optional: true
+ vitest@3.2.4:
+ resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.2.4
+ '@vitest/ui': 3.2.4
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
vscode-jsonrpc@8.2.0:
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
engines: {node: '>=14.0.0'}
@@ -6593,6 +7003,7 @@ packages:
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
+ deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
@@ -6623,6 +7034,11 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -6669,6 +7085,9 @@ packages:
peerDependencies:
yjs: ^13.0.0
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
yjs@13.6.28:
resolution: {integrity: sha512-EgnDOXs8+hBVm6mq3/S89Kiwzh5JRbn7w2wXwbrMRyKy/8dOFsLvuIfC+x19ZdtaDc0tA9rQmdZzbqqNHG44wA==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
@@ -6708,6 +7127,8 @@ packages:
snapshots:
+ '@adobe/css-tools@4.4.4': {}
+
'@ai-sdk/gateway@2.0.23(zod@4.2.1)':
dependencies:
'@ai-sdk/provider': 2.0.0
@@ -6836,8 +7257,120 @@ snapshots:
'@types/react': 19.2.7
react: 19.2.3
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.0': {}
+
+ '@babel/core@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.0
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helpers': 7.28.6
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.0':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.29.0
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.28.6': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.6':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+
+ '@babel/parser@7.29.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
'@babel/runtime@7.28.4': {}
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+
+ '@babel/traverse@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.0
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
'@biomejs/biome@2.1.2':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.1.2
@@ -7434,9 +7967,9 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@fumadocs/ui@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)':
+ '@fumadocs/ui@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)':
dependencies:
- fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
+ fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
lodash.merge: 4.6.2
next-themes: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
postcss-selector-parser: 7.1.1
@@ -7445,7 +7978,7 @@ snapshots:
tailwind-merge: 3.4.0
optionalDependencies:
'@types/react': 19.2.7
- next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ next: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
tailwindcss: 4.1.18
transitivePeerDependencies:
- '@mixedbread/sdk'
@@ -7860,6 +8393,8 @@ snapshots:
'@parcel/watcher-win32-ia32': 2.5.1
'@parcel/watcher-win32-x64': 2.5.1
+ '@polka/url@1.0.0-next.29': {}
+
'@posthog/core@1.15.0':
dependencies:
cross-spawn: 7.0.6
@@ -8795,6 +9330,8 @@ snapshots:
'@remirror/core-constants@3.0.0': {}
+ '@rolldown/pluginutils@1.0.0-beta.27': {}
+
'@rollup/rollup-android-arm-eabi@4.55.1':
optional: true
@@ -9096,6 +9633,40 @@ snapshots:
'@tanstack/table-core@8.21.3': {}
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/runtime': 7.28.4
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.4.4
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@testing-library/dom': 10.4.1
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ optionalDependencies:
+ '@types/react': 19.2.7
+ '@types/react-dom': 19.2.3(@types/react@19.2.7)
+
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
+ dependencies:
+ '@testing-library/dom': 10.4.1
+
'@tiptap/core@3.14.0(@tiptap/pm@3.14.0)':
dependencies:
'@tiptap/pm': 3.14.0
@@ -9205,8 +9776,36 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
'@types/canvas-confetti@1.9.0': {}
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
'@types/d3-array@3.2.2': {}
'@types/d3-axis@3.0.6':
@@ -9328,6 +9927,8 @@ snapshots:
dependencies:
'@types/ms': 2.1.0
+ '@types/deep-eql@4.0.2': {}
+
'@types/diff-match-patch@1.0.36': {}
'@types/estree-jsx@1.0.5':
@@ -9556,6 +10157,71 @@ snapshots:
'@vercel/oidc@3.0.5': {}
+ '@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
+ '@rolldown/pluginutils': 1.0.0-beta.27
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/expect@3.2.4':
+ dependencies:
+ '@types/chai': 5.2.3
+ '@vitest/spy': 3.2.4
+ '@vitest/utils': 3.2.4
+ chai: 5.3.3
+ tinyrainbow: 2.0.0
+
+ '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))':
+ dependencies:
+ '@vitest/spy': 3.2.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+
+ '@vitest/pretty-format@3.2.4':
+ dependencies:
+ tinyrainbow: 2.0.0
+
+ '@vitest/runner@3.2.4':
+ dependencies:
+ '@vitest/utils': 3.2.4
+ pathe: 2.0.3
+ strip-literal: 3.1.0
+
+ '@vitest/snapshot@3.2.4':
+ dependencies:
+ '@vitest/pretty-format': 3.2.4
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@3.2.4':
+ dependencies:
+ tinyspy: 4.0.4
+
+ '@vitest/ui@3.2.4(vitest@3.2.4)':
+ dependencies:
+ '@vitest/utils': 3.2.4
+ fflate: 0.8.2
+ flatted: 3.3.3
+ pathe: 2.0.3
+ sirv: 3.0.2
+ tinyglobby: 0.2.15
+ tinyrainbow: 2.0.0
+ vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(tsx@4.21.0)
+
+ '@vitest/utils@3.2.4':
+ dependencies:
+ '@vitest/pretty-format': 3.2.4
+ loupe: 3.2.1
+ tinyrainbow: 2.0.0
+
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -9591,16 +10257,24 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ ansi-regex@5.0.1: {}
+
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
+ ansi-styles@5.2.0: {}
+
argparse@2.0.1: {}
aria-hidden@1.2.6:
dependencies:
tslib: 2.8.1
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
aria-query@5.3.2: {}
array-buffer-byte-length@1.0.2:
@@ -9670,6 +10344,8 @@ snapshots:
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
+ assertion-error@2.0.1: {}
+
assistant-cloud@0.1.12:
dependencies:
assistant-stream: 0.2.46
@@ -9738,6 +10414,14 @@ snapshots:
dependencies:
fill-range: 7.1.1
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.11
+ caniuse-lite: 1.0.30001761
+ electron-to-chromium: 1.5.283
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
buffer-from@1.1.2: {}
buffer@5.7.1:
@@ -9746,6 +10430,8 @@ snapshots:
ieee754: 1.2.1
optional: true
+ cac@6.7.14: {}
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -9771,6 +10457,14 @@ snapshots:
ccount@2.0.1: {}
+ chai@5.3.3:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.3
+ deep-eql: 5.0.2
+ loupe: 3.2.1
+ pathval: 2.0.1
+
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -9792,6 +10486,8 @@ snapshots:
character-reference-invalid@2.0.1: {}
+ check-error@2.1.3: {}
+
chevrotain-allstar@0.3.1(chevrotain@11.0.3):
dependencies:
chevrotain: 11.0.3
@@ -9869,6 +10565,8 @@ snapshots:
confbox@0.1.8: {}
+ convert-source-map@2.0.0: {}
+
core-js@3.47.0: {}
cose-base@1.0.3:
@@ -9891,6 +10589,8 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css.escape@1.5.1: {}
+
cssesc@3.0.0: {}
cssstyle@4.6.0:
@@ -10134,6 +10834,8 @@ snapshots:
mimic-response: 3.1.0
optional: true
+ deep-eql@5.0.2: {}
+
deep-extend@0.6.0:
optional: true
@@ -10175,6 +10877,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dompurify@3.3.1:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -10206,6 +10912,8 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
+ electron-to-chromium@1.5.283: {}
+
emblor@1.4.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@radix-ui/react-dialog': 1.0.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -10320,6 +11028,8 @@ snapshots:
iterator.prototype: 1.1.5
safe-array-concat: 1.1.3
+ es-module-lexer@1.7.0: {}
+
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -10445,6 +11155,8 @@ snapshots:
'@esbuild/win32-ia32': 0.27.2
'@esbuild/win32-x64': 0.27.2
+ escalade@3.2.0: {}
+
escape-string-regexp@4.0.0: {}
escape-string-regexp@5.0.0: {}
@@ -10689,6 +11401,8 @@ snapshots:
expand-template@2.0.3:
optional: true
+ expect-type@1.3.0: {}
+
extend@3.0.2: {}
fast-deep-equal@3.1.3: {}
@@ -10721,6 +11435,8 @@ snapshots:
fflate@0.4.8: {}
+ fflate@0.8.2: {}
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -10777,7 +11493,7 @@ snapshots:
fsevents@2.3.3:
optional: true
- fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1):
+ fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1):
dependencies:
'@formatjs/intl-localematcher': 0.7.2
'@orama/orama': 3.1.18
@@ -10800,21 +11516,21 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.7
lucide-react: 0.477.0(react@19.2.3)
- next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ next: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
zod: 4.2.1
transitivePeerDependencies:
- supports-color
- fumadocs-mdx@14.2.1(fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)):
+ fumadocs-mdx@14.2.1(fumadocs-core@16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)):
dependencies:
'@mdx-js/mdx': 3.1.1
'@standard-schema/spec': 1.1.0
chokidar: 5.0.0
esbuild: 0.27.2
estree-util-value-to-estree: 3.5.0
- fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
+ fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
js-yaml: 4.1.1
mdast-util-to-markdown: 2.1.2
picocolors: 1.1.1
@@ -10828,15 +11544,15 @@ snapshots:
vfile: 6.0.3
zod: 4.2.1
optionalDependencies:
- next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ next: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
vite: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
transitivePeerDependencies:
- supports-color
- fumadocs-ui@16.3.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1):
+ fumadocs-ui@16.3.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1):
dependencies:
- '@fumadocs/ui': 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)
+ '@fumadocs/ui': 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(zod@4.2.1)
'@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -10848,7 +11564,7 @@ snapshots:
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3)
'@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
class-variance-authority: 0.7.1
- fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
+ fumadocs-core: 16.3.1(@types/react@19.2.7)(lucide-react@0.477.0(react@19.2.3))(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@4.2.1)
next-themes: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
@@ -10883,12 +11599,14 @@ snapshots:
functions-have-names@1.2.3: {}
- geist@1.5.1(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)):
+ geist@1.5.1(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)):
dependencies:
- next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ next: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
generator-function@2.0.1: {}
+ gensync@1.0.0-beta.2: {}
+
get-east-asian-width@1.4.0: {}
get-intrinsic@1.3.0:
@@ -11239,6 +11957,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ indent-string@4.0.0: {}
+
inherits@2.0.4:
optional: true
@@ -11419,21 +12139,25 @@ snapshots:
jiti@2.6.1: {}
- jotai-tanstack-query@0.11.0(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(jotai@2.16.0(@types/react@19.2.7)(react@19.2.3))(react@19.2.3):
+ jotai-tanstack-query@0.11.0(@tanstack/query-core@5.90.12)(@tanstack/react-query@5.90.12(react@19.2.3))(jotai@2.16.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.7)(react@19.2.3))(react@19.2.3):
dependencies:
'@tanstack/query-core': 5.90.12
- jotai: 2.16.0(@types/react@19.2.7)(react@19.2.3)
+ jotai: 2.16.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.7)(react@19.2.3)
optionalDependencies:
'@tanstack/react-query': 5.90.12(react@19.2.3)
react: 19.2.3
- jotai@2.16.0(@types/react@19.2.7)(react@19.2.3):
+ jotai@2.16.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.7)(react@19.2.3):
optionalDependencies:
+ '@babel/core': 7.29.0
+ '@babel/template': 7.28.6
'@types/react': 19.2.7
react: 19.2.3
js-tokens@4.0.0: {}
+ js-tokens@9.0.1: {}
+
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -11466,6 +12190,8 @@ snapshots:
- supports-color
- utf-8-validate
+ jsesc@3.1.0: {}
+
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
@@ -11478,6 +12204,8 @@ snapshots:
dependencies:
minimist: 1.2.8
+ json5@2.2.3: {}
+
jsondiffpatch@0.6.0:
dependencies:
'@types/diff-match-patch': 1.0.36
@@ -11601,6 +12329,8 @@ snapshots:
dependencies:
js-tokens: 4.0.0
+ loupe@3.2.1: {}
+
lowlight@1.20.0:
dependencies:
fault: 1.0.4
@@ -11608,6 +12338,10 @@ snapshots:
lru-cache@10.4.3: {}
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
lucide-react@0.477.0(react@19.2.3):
dependencies:
react: 19.2.3
@@ -11616,6 +12350,8 @@ snapshots:
dependencies:
react: 19.2.3
+ lz-string@1.5.0: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -12159,6 +12895,8 @@ snapshots:
mimic-response@3.1.0:
optional: true
+ min-indent@1.0.1: {}
+
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
@@ -12193,6 +12931,8 @@ snapshots:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
+ mrmime@2.0.1: {}
+
ms@2.1.3: {}
nanoid@3.3.11: {}
@@ -12210,13 +12950,13 @@ snapshots:
next-intl-swc-plugin-extractor@4.6.1: {}
- next-intl@4.6.1(next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3):
+ next-intl@4.6.1(next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3):
dependencies:
'@formatjs/intl-localematcher': 0.5.10
'@parcel/watcher': 2.5.1
'@swc/core': 1.15.7
negotiator: 1.0.0
- next: 16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ next: 16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-intl-swc-plugin-extractor: 4.6.1
po-parser: 2.0.0
react: 19.2.3
@@ -12231,7 +12971,7 @@ snapshots:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
- next@16.1.0(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ next@16.1.0(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@next/env': 16.1.0
'@swc/helpers': 0.5.15
@@ -12240,7 +12980,7 @@ snapshots:
postcss: 8.4.31
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
- styled-jsx: 5.1.6(react@19.2.3)
+ styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 16.1.0
'@next/swc-darwin-x64': 16.1.0
@@ -12263,6 +13003,8 @@ snapshots:
node-addon-api@7.1.1: {}
+ node-releases@2.0.27: {}
+
npm-to-yarn@3.0.1: {}
number-flow@0.5.8:
@@ -12392,6 +13134,8 @@ snapshots:
pathe@2.0.3: {}
+ pathval@2.0.1: {}
+
pg-cloudflare@1.2.7:
optional: true
@@ -12524,6 +13268,12 @@ snapshots:
prelude-ls@1.2.1: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
prismjs@1.27.0: {}
prismjs@1.30.0: {}
@@ -12725,6 +13475,8 @@ snapshots:
react-is@16.13.1: {}
+ react-is@17.0.2: {}
+
react-json-view-lite@2.5.0(react@19.2.3):
dependencies:
react: 19.2.3
@@ -12757,6 +13509,8 @@ snapshots:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
+ react-refresh@0.17.0: {}
+
react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3):
dependencies:
react: 19.2.3
@@ -12869,6 +13623,11 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -13262,6 +14021,8 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
+ siginfo@2.0.0: {}
+
simple-concat@1.0.1:
optional: true
@@ -13272,6 +14033,12 @@ snapshots:
simple-concat: 1.0.1
optional: true
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
@@ -13296,6 +14063,10 @@ snapshots:
stable-hash@0.0.5: {}
+ stackback@0.0.2: {}
+
+ std-env@3.10.0: {}
+
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -13394,11 +14165,19 @@ snapshots:
strip-bom@3.0.0: {}
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
strip-json-comments@2.0.1:
optional: true
strip-json-comments@3.1.1: {}
+ strip-literal@3.1.0:
+ dependencies:
+ js-tokens: 9.0.1
+
style-to-js@1.1.21:
dependencies:
style-to-object: 1.0.14
@@ -13407,10 +14186,12 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(react@19.2.3):
+ styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.3):
dependencies:
client-only: 0.0.1
react: 19.2.3
+ optionalDependencies:
+ '@babel/core': 7.29.0
stylis@4.3.6: {}
@@ -13459,6 +14240,10 @@ snapshots:
throttleit@2.1.0: {}
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.2: {}
+
tinyexec@1.0.2: {}
tinyglobby@0.2.15:
@@ -13466,6 +14251,12 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
+ tinypool@1.1.1: {}
+
+ tinyrainbow@2.0.0: {}
+
+ tinyspy@4.0.4: {}
+
tldts-core@6.1.86: {}
tldts@6.1.86:
@@ -13476,6 +14267,8 @@ snapshots:
dependencies:
is-number: 7.0.0
+ totalist@3.0.1: {}
+
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.86
@@ -13642,6 +14435,12 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
'@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@@ -13721,6 +14520,27 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ vite-node@3.2.4(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.3
+ es-module-lexer: 1.7.0
+ pathe: 2.0.3
+ vite: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0):
dependencies:
esbuild: 0.27.2
@@ -13736,6 +14556,50 @@ snapshots:
lightningcss: 1.30.2
tsx: 4.21.0
+ vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.27)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(tsx@4.21.0):
+ dependencies:
+ '@types/chai': 5.2.3
+ '@vitest/expect': 3.2.4
+ '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
+ '@vitest/pretty-format': 3.2.4
+ '@vitest/runner': 3.2.4
+ '@vitest/snapshot': 3.2.4
+ '@vitest/spy': 3.2.4
+ '@vitest/utils': 3.2.4
+ chai: 5.3.3
+ debug: 4.4.3
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.10.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinyglobby: 0.2.15
+ tinypool: 1.1.1
+ tinyrainbow: 2.0.0
+ vite: 7.3.1(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ vite-node: 3.2.4(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/debug': 4.1.12
+ '@types/node': 20.19.27
+ '@vitest/ui': 3.2.4(vitest@3.2.4)
+ jsdom: 25.0.1
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
vscode-jsonrpc@8.2.0: {}
vscode-languageserver-protocol@3.17.5:
@@ -13821,6 +14685,11 @@ snapshots:
dependencies:
isexe: 2.0.0
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
word-wrap@1.2.5: {}
wrappy@1.0.2:
@@ -13848,6 +14717,8 @@ snapshots:
lib0: 0.2.115
yjs: 13.6.28
+ yallist@3.1.1: {}
+
yjs@13.6.28:
dependencies:
lib0: 0.2.115
diff --git a/surfsense_web/vitest.config.ts b/surfsense_web/vitest.config.ts
new file mode 100644
index 000000000..34c28aa88
--- /dev/null
+++ b/surfsense_web/vitest.config.ts
@@ -0,0 +1,29 @@
+import { defineConfig } from 'vitest/config';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: './vitest.setup.ts',
+ css: true,
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html'],
+ exclude: [
+ 'node_modules/',
+ 'vitest.setup.ts',
+ '**/*.config.ts',
+ '**/*.d.ts',
+ '**/types/**',
+ ],
+ },
+ },
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './'),
+ },
+ },
+});
diff --git a/surfsense_web/vitest.setup.ts b/surfsense_web/vitest.setup.ts
new file mode 100644
index 000000000..f5c877301
--- /dev/null
+++ b/surfsense_web/vitest.setup.ts
@@ -0,0 +1,43 @@
+import '@testing-library/jest-dom';
+import { cleanup } from '@testing-library/react';
+import { afterEach, vi } from 'vitest';
+import React from 'react';
+
+// Cleanup after each test
+afterEach(() => {
+ cleanup();
+});
+
+// Mock ResizeObserver (required for Radix UI components)
+global.ResizeObserver = class ResizeObserver {
+ observe() { }
+ unobserve() { }
+ disconnect() { }
+};
+
+// Mock PointerEvent APIs for Radix UI Select
+HTMLElement.prototype.hasPointerCapture = vi.fn(() => false);
+HTMLElement.prototype.setPointerCapture = vi.fn();
+HTMLElement.prototype.releasePointerCapture = vi.fn();
+HTMLElement.prototype.scrollIntoView = vi.fn();
+
+// Mock Next.js router
+vi.mock('next/navigation', () => ({
+ useRouter: () => ({
+ push: vi.fn(),
+ replace: vi.fn(),
+ prefetch: vi.fn(),
+ back: vi.fn(),
+ pathname: '/',
+ query: {},
+ }),
+ usePathname: () => '/',
+ useSearchParams: () => new URLSearchParams(),
+}));
+
+// Mock Next.js Image component
+vi.mock('next/image', () => ({
+ default: (props: any) => {
+ return React.createElement('img', props);
+ },
+}));