diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..4ac29ba1f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+venv/
+backend/
+frontend/migrations/
+frontend/node_modules/
+frontend/.next/
+frontend/.env.local
\ No newline at end of file
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 223b4737d..928a55dc4 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -5,6 +5,100 @@
Below, sketch (ASCII, hand-drawn JPEG/PNG pasted in, or ASCII art) the high-level components of your agent.
+life-witness-agent/
+├── frontend/
+│ ├── src/
+│ │ ├── components/
+│ │ │ ├── VoiceInterface.jsx # Voice input/output UI component
+│ │ │ ├── PhotoUpload.jsx # Drag & drop photo upload interface
+│ │ │ ├── MemoryTimeline.jsx # Timeline view of life events
+│ │ │ ├── MemoryCard.jsx # Individual memory display card
+│ │ │ └── AgentStatus.jsx # Real-time agent activity indicator
+│ │ ├── services/
+│ │ │ ├── apiClient.js # HTTP client for backend API calls
+│ │ │ ├── voiceService.js # Web Speech API wrapper
+│ │ │ ├── uploadService.js # File upload handling
+│ │ │ └── websocketClient.js # Real-time agent communication
+│ │ ├── utils/
+│ │ │ ├── audioUtils.js # Audio processing utilities
+│ │ │ ├── imageUtils.js # Image compression/validation
+│ │ │ └── formatters.js # Data formatting helpers (optional)
+│ │ ├── App.jsx # Main React application component
+│ │ └── main.jsx # React app entry point
+│ ├── public/
+│ │ ├── index.html # HTML template
+│ │ └── manifest.json # PWA manifest
+│ ├── package.json # Frontend dependencies
+│ └── vite.config.js # Vite build configuration (Or similar config file)
+├── backend/
+│ ├── agents/
+│ │ ├── base_agent.py # Abstract base class for all agents (Should be implemented first)
+│ │ ├── planner_agent.py # Intent parsing and task planning
+│ │ ├── voice_agent.py # Speech processing and TTS
+│ │ ├── vision_agent.py # Image/video analysis via Gemini
+│ │ ├── context_agent.py # Gmail/Calendar data integration
+│ │ ├── memory_agent.py # Memory storage and retrieval
+│ │ ├── insight_agent.py # Pattern recognition and analysis
+│ │ └── response_agent.py # Conversational response generation
+│ ├── orchestration/
+│ │ ├── orchestrator.py # Main agent coordination engine (using Langgraph or the execution engine)
+│ │ ├── event_bus.py # Event-driven communication system (optional)
+│ │ ├── task_queue.py # Async task management
+│ │ └── agent_pool.py # Agent instance management
+│ ├── services/
+│ │ ├── gemini_service.py # Gemini API client and utilities
+│ │ ├── gmail_service.py # Gmail API integration
+│ │ ├── calendar_service.py # Google Calendar API integration
+│ │ └── storage_service.py # File system and JSON persistence (Need other storage services like vertex or bigquery?)
+│ ├── utils/
+│ │ ├── logging_config.py # Structured logging setup
+│ │ ├── tracing.py # Distributed tracing utilities (Optional)
+│ │ ├── metrics.py # Performance monitoring (Optional)
+│ │ ├── error_handling.py # Exception handling patterns (Optional)
+│ │ └── config.py # Environment configuration
+│ ├── api/
+│ │ ├── routes/
+│ │ │ ├── memory_routes.py # Memory CRUD API endpoints
+│ │ │ ├── voice_routes.py # Voice processing endpoints
+│ │ │ ├── upload_routes.py # File upload endpoints
+│ │ │ └── query_routes.py # Memory query endpoints
+│ │ └── middleware.py # Authentication and logging middleware (Skip for now)
+│ ├── main.py # FastAPI application entry point
+│ └── requirements.txt # Python dependencies
+├── data/
+│ ├── memories/
+│ │ ├── events.json # Episodic memory storage (Can be temporal storage)
+│ │ ├── relationships.json # Person/place relationship graph (Not required for now)
+│ │ └── embeddings.json # Vector embeddings for search
+│ ├── cache/
+│ │ └── api_cache.json # Cached API responses
+│ └── demo/
+│ ├── sample_memories.json # Pre-loaded demo data
+│ └── demo_photos/ # Sample images for demo
+├── tests/
+│ ├── unit/
+│ │ ├── test_agents.py # Unit tests for agent classes
+│ │ ├── test_orchestrator.py # Orchestration logic tests
+│ │ └── test_services.py # Service layer tests
+│ ├── integration/
+│ │ ├── test_api_endpoints.py # API integration tests
+│ │ └── test_agent_coordination.py # Multi-agent workflow tests
+│ └── fixtures/
+│ ├── sample_data.py # Test data fixtures
+│ └── mock_responses.py # Mock API responses
+├── scripts/ # Optional
+│ ├── setup_dev.sh # Development environment setup
+│ ├── run_demo.sh # Demo mode launcher
+│ └── deploy.sh # Production deployment script
+├── .env.example # Environment variables template
+├── .gitignore # Git ignore patterns
+├── docker-compose.yml # Local development containers
+├── README.md # Project overview and setup instructions, dependencies and how to run the agent
+├── ARCHITECTURE.md # System architecture overview (This file)
+├── EXPLANATION.md # Natural Language Explanation of agent's reasoning, memory, planning, tools and limitations
+├── LICENSE # License and policy
+└── DEMO.md # Demo instructions and scenarios
+
## Components
1. **User Interface**
@@ -22,3 +116,231 @@ Below, sketch (ASCII, hand-drawn JPEG/PNG pasted in, or ASCII art) the high-leve
- Logging of each reasoning step
- Error handling / retries
+## Architecture Overview
+-------------------------
+1. Planner Agent (Brain)
+
+ Intent Analysis: Parses voice input to understand user goals
+ Task Decomposition: Breaks complex requests into agent-specific tasks
+ Agent Selection: Dynamically chooses which agents to activate
+ Execution Planning: Determines parallel vs sequential execution patterns
+
+2. Executor Engine (Coordinator)
+
+ Task Queue: Manages parallel/sequential agent execution
+ Agent Pool: Maintains agent instances and load balancing
+ Result Aggregation: Combines outputs from multiple agents
+ Error Handling: Graceful degradation when agents fail
+
+3. Memory Structure (Knowledge Base)
+
+ Event Storage: Structured life event data with rich metadata
+ Relationship Graph: Connections between people, places, activities
+ Semantic Index: Vector embeddings for intelligent search
+ -- Tiered Storage: Hot/Warm/Cold storage for performance optimization
+
+4. Specialized Agents (Workers)
+
+ Voice Agent: Speech-to-text, emotion detection, text-to-speech
+ Vision Agent: Photo analysis, object/person recognition
+ Context Agent: Gmail/Calendar integration, event correlation
+ -- Insight Agent: Pattern detection, relationship analysis
+ Response Agent: Conversational AI, personality, context-aware replies
+
+## Tool Integrations
+Gemini API
+
+ Multimodal Live API: Real-time voice and image processing
+ Function Calling: Direct integration with agent methods
+ Long Context: Maintains conversation and memory context
+ Vision Processing: Advanced photo and document analysis
+
+Google APIs
+
+ Gmail API: Event extraction from emails
+ Calendar API: Scheduled event correlation
+
+## Storage & Performance
+
+ JSON Files: Simple, easy to implement persistence
+ Vector Embeddings: Semantic search capabilities
+ Caching Layer: Performance optimization
+ Logging layer: In-memory logs for fault tolerance
+
+Metrics Collection
+
+ Agent response times and success rates
+ API usage and rate limiting
+ Memory query performance
+ -- User interaction patterns
+
+Logging Architecture
+
+ Structured JSON logs with trace IDs
+ Request/response payload logging (sanitized)
+ -- Error tracking with full context
+
+## Use cases:
+1. Conference memory
+Feeding to memory:
+🎤 YOU: "Hey Life Witness, I just met an amazing person at the conference"
+
+🤖 AGENT: "I'm listening! Tell me about this person while it's fresh in your memory."
+
+🎤 YOU: [Uploads photo of you with a woman at a conference booth]
+ "This is Jennifer Chen, she's a VP of Engineering at Stripe. We talked about
+ their new payment APIs and she mentioned they're hiring senior engineers.
+ She gave me her card and said to reach out about the role."
+
+🤖 AGENT: "I can see you're both at the Stripe booth - that's perfect context!
+ Let me capture this. I notice this is day 2 of the TechCrunch conference
+ based on your calendar. Should I also note that she specifically
+ mentioned hiring for senior engineers?"
+
+🎤 YOU: "Yes, and she said their team is working on crypto payments. Also,
+ she went to Stanford like me - we bonded over that."
+
+🤖 AGENT: "Perfect! I've saved this memory with rich context:
+ • Person: Jennifer Chen, VP Engineering at Stripe
+ • Connection: Stanford alumni, both interested in crypto
+ • Opportunity: Senior engineering role on crypto payments team
+ • Action item: Follow up about the position
+ • Location: TechCrunch Conference, Stripe booth
+ • Relationship strength: Strong initial connection
+
+ Should I remind you to follow up with her next week?"
+
+🎤 YOU: "Yes, remind me Tuesday morning."
+
+🤖 AGENT: "Calendar reminder set for Tuesday 9 AM. I'll include her contact details
+ and our conversation context when I remind you."
+
+Query on memory:
+🎤 YOU: "Who was that person I met at TechCrunch who worked on crypto payments?"
+
+🤖 AGENT: "That would be Jennifer Chen, VP of Engineering at Stripe! You met her
+ at their booth on day 2 of TechCrunch conference. She mentioned they were
+ hiring senior engineers for their crypto payments team."
+
+🎤 YOU: "Right! What else did we talk about?"
+
+🤖 AGENT: "You both bonded over being Stanford alumni, and she specifically
+ mentioned their new payment APIs. You took a photo together at their booth,
+ and she gave you her business card with instructions to reach out about
+ the senior engineering role."
+
+🎤 YOU: "Did I ever follow up with her?"
+
+🤖 AGENT: "Yes, I reminded you on Tuesday morning as requested, and you sent her
+ a LinkedIn message that same day. She responded within 2 hours suggesting
+ a coffee meeting, which you scheduled for the next Friday. You met at
+ Blue Bottle Coffee downtown and had what you described as a 'great conversation
+ about team culture and technical challenges.'"
+
+🎤 YOU: "Perfect. I'm interviewing at Coinbase tomorrow and want to mention
+ my connection to Stripe's crypto work. Can you give me some talking points?"
+
+🤖 AGENT: "Based on your conversation with Jennifer, here are some relevant points:
+ • Stripe is actively investing in crypto infrastructure with dedicated teams
+ • They're focusing on enterprise-grade payment solutions (Jennifer's emphasis)
+ • Their approach prioritizes regulatory compliance and security
+ • Jennifer mentioned they're seeing huge demand from fintech companies
+
+ This shows you're plugged into industry trends and have insights from
+ a senior leader in the space. Would you like me to find other crypto-related
+ conversations from your memory?"
+
+
+2. Birthday event memory
+Feeding in
+🎤 YOU: "Life Witness, I want to remember this moment"
+
+🤖 AGENT: "I'm here! What's happening right now?"
+
+🎤 YOU: [Uploads video of nephew blowing out candles]
+ "It's my nephew Jake's 8th birthday party. He just blew out the candles
+ and made a wish. My sister Sarah organized this whole Minecraft-themed party
+ in their backyard. Jake's been obsessed with Minecraft all year."
+
+🤖 AGENT: "I can see the Minecraft decorations and Jake's huge smile! This looks
+ like such a special moment. I notice your sister really went all out
+ with the theme - there are Minecraft blocks everywhere!"
+
+🎤 YOU: "Yeah, she's amazing at this stuff. Jake was so excited he couldn't
+ even speak when he saw the cake - it was shaped like a Creeper.
+ And all his school friends are here running around in Minecraft costumes."
+
+🤖 AGENT: "That's so sweet! I can hear the joy in your voice. Should I also note
+ that this is at your sister's house in Berkeley? I see from your calendar
+ this party was planned for 2 PM today."
+
+🎤 YOU: "Yes, and I brought him that Lego Minecraft set he's been wanting.
+ His reaction was priceless - he literally jumped up and down.
+ These are the moments I never want to forget."
+
+🤖 AGENT: "I've captured this beautiful memory
+ • Event: Jake's 8th birthday party, Minecraft theme
+ • Location: Sister Sarah's backyard, Berkeley
+ • Key moments: Candle blowing, Creeper cake, costume parade
+ • Your gift: Lego Minecraft set - huge success!
+ • Emotional context: Pure joy, family connection, pride in sister's effort
+ • Attendees: School friends, family
+
+ This feels like one of those core family memories. I'll make sure it's
+ easy to find when you want to remember these precious moments with Jake."
+
+Query on:
+🎤 YOU: "What did Jake love most about his last birthday party?"
+
+🤖 AGENT: "At Jake's 8th birthday last March, he absolutely loved the Minecraft theme!
+ Your sister Sarah went all out with decorations, and the Creeper-shaped cake
+ was a huge hit. But what really made him light up was the Lego Minecraft set
+ you brought - he literally jumped up and down with excitement."
+
+🎤 YOU: "What else was he into around that time?"
+
+🤖 AGENT: "Based on your memories from that period, Jake was completely obsessed with
+ Minecraft - you mentioned he'd been into it 'all year.' But I also have
+ a memory from two weeks after his party where you took him to the science
+ museum, and he was fascinated by the robotics exhibit. He kept asking
+ how robots are programmed."
+
+🎤 YOU: "Oh right! Has he mentioned robots since then?"
+
+🤖 AGENT: "Yes! In July, you attended his 'invention presentation' at summer camp
+ where he built a simple robot car. You were so proud - you said he
+ explained the programming logic like a little engineer. And just last month
+ at Thanksgiving, he showed everyone a robot he coded to draw patterns."
+
+🎤 YOU: "Wow, I didn't realize he'd gotten so into robotics. What should I get
+ him for his 9th birthday?"
+
+🤖 AGENT: "Based on his evolution from Minecraft to robotics, I'd suggest something
+ that bridges both interests:
+
+ • A programmable Minecraft robot kit (combines both passions)
+ • Lego Mindstorms (he loved the Lego Minecraft set, now with programming)
+ • A beginner Arduino kit with Minecraft-themed projects
+
+ Also, remember how much he loved explaining his robot car? He seems to
+ enjoy teaching others, so maybe something he can demonstrate to friends
+ would be perfect. Want me to check what specific robotics projects he's
+ mentioned recently?"
+
+🎤 YOU: "That's brilliant! Can you remind me what Sarah did for decorations last
+ year? I want to help her plan something even better."
+
+🤖 AGENT: "Sarah created an amazing Minecraft wonderland in her Berkeley backyard:
+ • Cardboard Minecraft blocks scattered everywhere
+ • Creeper-shaped birthday cake (huge hit)
+ • All the kids wore Minecraft costumes
+ • Green and brown color scheme
+
+ For a robotics theme this year, you could suggest:
+ • Silver and blue decorations (tech colors)
+ • Robot-shaped cake or cupcakes that look like circuit boards
+ • A 'robot building station' where kids can assemble simple bots
+ • Maybe coordinate with that science museum he loved?
+
+ Sarah clearly has amazing party planning skills - she'll probably love
+ collaborating on this evolution from Minecraft to robotics!"
\ No newline at end of file
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
new file mode 100644
index 000000000..8b8dd4b2b
--- /dev/null
+++ b/DEPLOYMENT.md
@@ -0,0 +1,144 @@
+# Deployment Guide: agents-assemble
+`Tharak Vangalapat`
+
+This guide describes how to build, deploy, and access both backend (FastAPI) and frontend (Next.js/React) services for the agents-assemble project on Google Cloud Run using Docker.
+
+---
+
+## Prerequisites
+- Google Cloud account and project
+- `gcloud` CLI installed and authenticated
+- Docker installed
+- Node.js and Python installed locally for development
+- Required files: `Dockerfile`, `requirements.txt` (backend), `package.json` (frontend), `.env.local` (frontend)
+
+---
+
+## 1. Build and Deploy Backend (FastAPI)
+
+### a. Ensure dependencies
+Your `backend/requirements.txt` should include:
+```
+fastapi
+pydantic
+uvicorn[standard]
+```
+
+### b. Build Docker image locally (optional)
+```sh
+cd agents-assemble/backend
+docker build -t life-witness-agent-backend .
+```
+
+### c. Deploy to Google Cloud Run
+From the project root, run:
+```sh
+gcloud builds submit --tag gcr.io/$PROJECT_ID/life-witness-agent-backend ./backend
+
+gcloud run deploy life-witness-agent-backend \
+ --image gcr.io/$PROJECT_ID/life-witness-agent-backend \
+ --platform managed \
+ --region us-central1 \
+ --allow-unauthenticated
+```
+
+---
+
+## 2. Build and Deploy Frontend (Next.js/React)
+
+### a. Ensure `.env.local` is present in `frontend/` and not excluded by `.dockerignore`.
+
+### b. Build Docker image locally (optional)
+```sh
+cd agents-assemble/frontend
+docker build -t life-witness-agent-frontend .
+```
+
+### c. Deploy to Google Cloud Run
+From the project root, run:
+```sh
+gcloud builds submit --tag gcr.io/$PROJECT_ID/life-witness-agent-frontend ./frontend
+
+gcloud run deploy life-witness-agent-frontend \
+ --image gcr.io/$PROJECT_ID/life-witness-agent-frontend \
+ --platform managed \
+ --region us-central1 \
+ --allow-unauthenticated
+```
+
+---
+
+## 3. Accessing the Services
+- **Backend API Docs:**
+ Visit the Cloud Run URL for `life-witness-agent-backend` (e.g., `https://life-witness-agent-backend-xxxxxx.a.run.app/docs`).
+- **Frontend App:**
+ Visit the Cloud Run URL for `life-witness-agent-frontend` (e.g., `https://life-witness-agent-frontend-xxxxxx.a.run.app`).
+
+---
+
+## 4. Environment Variables
+- **Frontend:**
+ Set `NEXT_PUBLIC_AGENT_API_URL` in `.env.local` to your backend Cloud Run URL.
+- **Backend:**
+ Use environment variables or Google Secret Manager for sensitive data if needed.
+
+---
+
+## 5. Troubleshooting
+- **500 Internal Server Error:**
+ Ensure all Python dependencies are installed and your backend code is correct.
+- **Memory Issues:**
+ Increase memory allocation with `--memory 1Gi` or higher in the deploy command.
+- **.env.local not found:**
+ Make sure it’s present in `frontend/` and not listed in `.dockerignore`.
+
+---
+
+## 6. Useful Commands
+- **List Cloud Run services:**
+ ```sh
+ gcloud run services list --region us-central1 --platform managed
+ ```
+- **Get service URL:**
+ ```sh
+ gcloud run services describe SERVICE_NAME --region us-central1 --platform managed --format 'value(status.url)'
+ ```
+
+---
+
+## 7. Updating Services
+After code changes, repeat the build and deploy steps for the affected service.
+
+---
+
+## 8. Running Deployment Scripts
+
+### a. Local Development
+To run both backend and frontend locally using Docker Compose:
+
+```sh
+cd agents-assemble
+# Start both services
+docker-compose up --build
+```
+
+Or, to use the provided demo script:
+```sh
+cd agents-assemble/scripts
+./setup_dev.sh # Set up Python environment and install backend dependencies
+./run_demo.sh # Start the FastAPI backend locally
+```
+
+### b. Cloud Deployment
+To build and deploy both backend and frontend to Google Cloud Run using the provided script:
+
+```sh
+cd agents-assemble/scripts
+./deploy.sh
+```
+
+This will build Docker images and deploy both services to Cloud Run. Make sure you have authenticated with `gcloud` and set your project.
+
+---
+
+For advanced topics (custom domains, load balancer, etc.), see Google Cloud Run documentation.
diff --git a/backend/agents/Docker b/backend/agents/Docker
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/agents/__pycache__/context_agent.cpython-313.pyc b/backend/agents/__pycache__/context_agent.cpython-313.pyc
new file mode 100644
index 000000000..7f8e3c38a
Binary files /dev/null and b/backend/agents/__pycache__/context_agent.cpython-313.pyc differ
diff --git a/backend/agents/base_agent.py b/backend/agents/base_agent.py
new file mode 100644
index 000000000..8d93fa02b
--- /dev/null
+++ b/backend/agents/base_agent.py
@@ -0,0 +1,42 @@
+from abc import ABC, abstractmethod
+
+class BaseAgent(ABC):
+ @abstractmethod
+ def run(self, input_text: str) -> str:
+ """Run the agent on the input text and return a response."""
+ pass
+
+class ReActAgent(BaseAgent):
+ def run(self, input_text: str) -> str:
+ # Simple demo: calculator tool
+ if "calculate" in input_text.lower():
+ expr = input_text.lower().replace("calculate", "").strip()
+ try:
+ result = eval(expr, {"__builtins__": {}})
+ return f"Result: {result}"
+ except Exception as e:
+ return f"Error: {str(e)}"
+ return "Sorry, I can only calculate for now."
+
+ def reason_and_act(self, user_input: str) -> dict:
+ # Simple demo: calculator tool
+ if "calculate" in user_input.lower():
+ expr = user_input.lower().replace("calculate", "").strip()
+ try:
+ result = eval(expr, {"__builtins__": {}})
+ return {
+ "thought": "I need to calculate.",
+ "action": "calculator",
+ "result": f"Result: {result}"
+ }
+ except Exception as e:
+ return {
+ "thought": "Tried to calculate but failed.",
+ "action": "calculator",
+ "result": f"Error: {str(e)}"
+ }
+ return {
+ "thought": "I don't know how to handle this.",
+ "action": None,
+ "result": "Sorry, I can only calculate for now."
+ }
\ No newline at end of file
diff --git a/backend/agents/context_agent.py b/backend/agents/context_agent.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/agents/insight_agent.py b/backend/agents/insight_agent.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/agents/memory_agent.py b/backend/agents/memory_agent.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/agents/planner_agent.py b/backend/agents/planner_agent.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/agents/response_agent.py b/backend/agents/response_agent.py
new file mode 100644
index 000000000..8144761be
--- /dev/null
+++ b/backend/agents/response_agent.py
@@ -0,0 +1,86 @@
+from langchain.llms.base import LLM
+from langchain.chains import LLMChain
+from langchain.prompts import PromptTemplate
+from base_agent import BaseAgent # your custom base class
+from langchain_google_vertexai import ChatVertexAI
+
+class ResponseAgent(BaseAgent):
+ def __init__(self):
+ # Initialize the Google Vertex AI chat model (Gemini)
+ self.llm = ChatVertexAI(
+ model_name="gemini-2.5-pro", # update model if needed
+ project="your-project-id", # replace with your GCP project ID
+ location="us-central1" # update region if needed
+ )
+
+ # Prompt template designed for Life Witness app with memory context
+ prompt_template = PromptTemplate(
+ input_variables=["input", "context"],
+ template="""
+You are Life Witness, a personal AI memory assistant that listens to life stories, captures moments, and helps recall memories with rich context.
+
+You have access to the user's life memories, including events, photos, calendar entries, and relationships.
+
+Respond in a warm, empathetic, and conversational tone — like a trusted friend who remembers important details.
+
+Use the provided context to ground your replies, including dates, locations, people, and emotional cues.
+
+If the user asks about past events, recall details clearly and helpfully.
+
+If the user wants to set reminders or follow up, help create clear action items.
+
+Do NOT say you are an AI or mention technical details.
+
+---
+
+Context:
+{context}
+
+User:
+{input}
+
+Life Witness:
+"""
+ )
+
+ self.chain = LLMChain(llm=self.llm, prompt=prompt_template)
+
+ def run(self, input_text: str, context: str = "") -> str:
+ """
+ Generate a conversational response using the input text and optional memory context.
+
+ Args:
+ input_text (str): The user query or statement.
+ context (str, optional): A string summarizing relevant memories or context. Defaults to "".
+
+ Returns:
+ str: The agent's generated response.
+ """
+ return self.chain.run(input=input_text, context=context)
+
+#Example Usage:
+
+"""
+response_agent = ResponseAgent()
+response = response_agent.run(user_input, memory_context)
+"""
+
+
+"""
+if __name__ == "__main__":
+ # Make sure you have authenticated your GCP credentials via:
+ # gcloud auth application-default login
+
+ print("Initializing ResponseAgent...")
+ agent = ResponseAgent()
+ print("Agent initialized.")
+
+ prompt = "Hello, how can you assist me today?"
+ print(f"Sending prompt: \"{prompt}\"")
+ try:
+ reply = agent.run(prompt)
+ print(f"Agent reply: {reply}")
+ except Exception as e:
+ print(f"An error occurred: {e}")
+ print("Check your Google Cloud authentication and project/location settings.")
+"""
diff --git a/backend/agents/vision_agent.py b/backend/agents/vision_agent.py
new file mode 100644
index 000000000..d966d13c5
--- /dev/null
+++ b/backend/agents/vision_agent.py
@@ -0,0 +1,64 @@
+from langchain_google_genai import ChatGoogleGenerativeAI
+from langchain_core.messages import HumanMessage
+from base_agent import BaseAgent
+from dotenv import load_dotenv
+import base64
+
+
+load_dotenv()
+
+
+class VisionAgent(BaseAgent):
+ def __init__(self, model_name: str = "gemini-2.5-flash"):
+ """
+ Initializes the VisionAgent with a specified Google Generative AI model.
+
+ Args:
+ model_name (str): The name of the model to use.
+ """
+ super().__init__()
+ self.model = ChatGoogleGenerativeAI(model=model_name)
+ print(f"VisionAgent initialized with model: {model_name}")
+
+ def describe(self, image_path: str) -> str:
+ """
+ Creates a detailed textual description of the image at the given file path.
+
+ Args:
+ image_path (str): The file path to the image to be described.
+
+ Returns:
+ str: A textual description of the image.
+ """
+ # Read the image file and encode it in base64
+ try:
+ with open(image_path, "rb") as image_file:
+ base64_image = base64.b64encode(image_file.read()).decode("utf-8")
+ except FileNotFoundError:
+ return f"Error: Image file not found at '{image_path}'"
+
+ # Prepare the message for the model
+ message = HumanMessage(
+ content=[
+ {"type": "text", "text": "Describe this image in detail."},
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
+ },
+ ]
+ )
+
+ # Invoke the model and return the content of the response
+ response = self.model.invoke([message])
+ return response.content
+
+if __name__=="__main__":
+
+ vision_agent = VisionAgent()
+
+ image_file_path = "path_to_image"
+
+ description = vision_agent.describe(image_file_path)
+
+ print("\n--- Image Description ---")
+ print(description)
diff --git a/backend/agents/voice_agent.py b/backend/agents/voice_agent.py
new file mode 100644
index 000000000..c8b7d0175
--- /dev/null
+++ b/backend/agents/voice_agent.py
@@ -0,0 +1,127 @@
+from base_agent import BaseAgent
+from google.cloud import speech, texttospeech
+import io
+import os
+import tempfile
+from pydub import AudioSegment
+
+
+class VoiceAgent(BaseAgent):
+ def __init__(self):
+ # Initialize Google Cloud clients
+ self.stt_client = speech.SpeechClient()
+ self.tts_client = texttospeech.TextToSpeechClient()
+
+
+ def speech_to_text(self, audio_file_path):
+ # Preprocess: convert to mono and 16kHz
+ print("[INFO] Preprocessing audio...")
+ sound = AudioSegment.from_file(audio_file_path)
+ sound = sound.set_channels(1).set_frame_rate(16000)
+
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
+ temp_path = tmp_file.name
+ sound.export(temp_path, format="wav")
+
+ print(f"[INFO] Processed audio saved to: {temp_path}")
+
+ # Load processed audio for Google STT
+ client = speech.SpeechClient()
+ with open(temp_path, "rb") as f:
+ audio_content = f.read()
+
+ audio = speech.RecognitionAudio(content=audio_content)
+ config = speech.RecognitionConfig(
+ encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
+ sample_rate_hertz=16000,
+ language_code="en-US",
+ )
+
+ response = client.recognize(config=config, audio=audio)
+
+ # Clean up temp file
+ os.remove(temp_path)
+
+ if response.results:
+ transcript = " ".join([result.alternatives[0].transcript for result in response.results])
+ return transcript
+ else:
+ return "No transcription available."
+
+
+ def text_to_speech(self, text: str, output_file_path: str = "output.mp3") -> str:
+ """
+ Converts text to speech audio using Google Cloud Text-to-Speech.
+ Saves audio to output_file_path and returns the path.
+ """
+ synthesis_input = texttospeech.SynthesisInput(text=text)
+
+ voice = texttospeech.VoiceSelectionParams(
+ language_code="en-US",
+ ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL,
+ )
+
+ audio_config = texttospeech.AudioConfig(
+ audio_encoding=texttospeech.AudioEncoding.MP3,
+ )
+
+ response = self.tts_client.synthesize_speech(
+ input=synthesis_input,
+ voice=voice,
+ audio_config=audio_config,
+ )
+
+ with open(output_file_path, "wb") as out:
+ out.write(response.audio_content)
+
+ return output_file_path
+
+ def run(self, input_data: str, mode: str = "text-to-speech") -> str:
+ """
+ Main run method to either convert text to speech or speech to text.
+ mode: 'text-to-speech' or 'speech-to-text'
+
+ If mode is 'speech-to-text', input_data should be path to audio file.
+ If mode is 'text-to-speech', input_data should be text to convert.
+
+ Returns transcript text (for speech-to-text) or output audio path (for text-to-speech).
+ """
+ if mode == "speech-to-text":
+ return self.speech_to_text(input_data)
+ elif mode == "text-to-speech":
+ return self.text_to_speech(input_data)
+ else:
+ raise ValueError("Invalid mode. Use 'text-to-speech' or 'speech-to-text'.")
+
+
+
+
+
+
+"""
+
+if __name__ == "__main__":
+ agent = VoiceAgent()
+
+ # Example 1: Convert speech audio file to text
+ original_audio = "/Users/rakeshcavala/agents-assemble/backend/agents/sample_audio.wav" # Input audio file
+
+ try:
+ # Preprocess audio for Google STT
+
+ # Run speech-to-text
+ transcript = agent.run(original_audio, mode="speech-to-text")
+ print(f"Transcript from '{original_audio}':\n{transcript}")
+ except Exception as e:
+ print(f"Error in speech-to-text: {e}")
+
+ # Example 2: Convert text to speech audio file
+ text_input = "Hello, this is a test of the Google Cloud Text-to-Speech API."
+ try:
+ output_audio = agent.run(text_input, mode="text-to-speech")
+ print(f"Audio content saved to: {output_audio}")
+ except Exception as e:
+ print(f"Error in text-to-speech: {e}")
+
+ """
+
diff --git a/backend/api/middleware.py b/backend/api/middleware.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/api/routes/memory_routes.py b/backend/api/routes/memory_routes.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/api/routes/query_routes.py b/backend/api/routes/query_routes.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/api/routes/upload_routes.py b/backend/api/routes/upload_routes.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/api/routes/voice_routes.py b/backend/api/routes/voice_routes.py
new file mode 100644
index 000000000..036794c93
--- /dev/null
+++ b/backend/api/routes/voice_routes.py
@@ -0,0 +1,144 @@
+# api/routes/voice_routes.py - Voice processing endpoints
+from fastapi import APIRouter, HTTPException, UploadFile, File, Depends
+from pydantic import BaseModel, Field
+from typing import Optional, Dict, Any
+import base64
+import logging
+
+# Import orchestrator
+from orchestration.orchestrator import Orchestrator
+
+logger = logging.getLogger(__name__)
+
+# Create router
+router = APIRouter(prefix="/api/voice", tags=["voice"])
+
+# Request/Response models
+class VoiceProcessRequest(BaseModel):
+ """Generic voice processing request"""
+ audio_data: Optional[str] = Field(None, description="Base64 encoded audio data")
+ text: Optional[str] = Field("", description="Text input if no audio")
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
+
+class VoiceProcessResponse(BaseModel):
+ """Generic voice processing response"""
+ response: str
+ voice_data: Optional[Dict[str, Any]] = None
+ insights: Optional[list] = None
+ memory_stored: Optional[bool] = None
+ memories_retrieved: Optional[list] = None
+ error: Optional[str] = None
+
+# Dependency to get orchestrator instance
+def get_orchestrator() -> Orchestrator:
+ """Get the orchestrator instance from app state"""
+ from main import app
+ if not hasattr(app.state, 'orchestrator'):
+ raise HTTPException(status_code=503, detail="Orchestrator not initialized")
+ return app.state.orchestrator
+
+@router.post("/process", response_model=VoiceProcessResponse)
+async def process_voice(
+ request: VoiceProcessRequest,
+ orchestrator: Orchestrator = Depends(get_orchestrator)
+):
+ """
+ Process voice input through the orchestrator
+
+ This endpoint simply forwards the request to the orchestrator
+ and returns its response.
+ """
+ try:
+ # Build orchestrator input
+ orchestrator_input = {
+ "text": request.text or "",
+ "audio_data": request.audio_data,
+ "metadata": request.metadata
+ }
+
+ # Process through orchestrator
+ result = await orchestrator.process(orchestrator_input)
+
+ # Return orchestrator response directly
+ return VoiceProcessResponse(
+ response=result.get("response", ""),
+ voice_data=result.get("voice_data"),
+ insights=result.get("insights"),
+ memory_stored=result.get("memory_stored"),
+ memories_retrieved=result.get("memories_retrieved"),
+ error=result.get("error")
+ )
+
+ except Exception as e:
+ logger.error(f"Voice processing error: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+# @router.post("/upload")
+# async def upload_voice_file(
+# file: UploadFile = File(...),
+# metadata: Optional[str] = None,
+# orchestrator: Orchestrator = Depends(get_orchestrator)
+# ):
+# """
+# Upload a voice file for processing
+
+# Converts the file to base64 and forwards to orchestrator.
+# """
+# try:
+# # Read and encode file
+# audio_bytes = await file.read()
+# audio_base64 = base64.b64encode(audio_bytes).decode('utf-8')
+
+# # Parse metadata if provided
+# import json
+# parsed_metadata = {}
+# if metadata:
+# try:
+# parsed_metadata = json.loads(metadata)
+# except:
+# pass
+
+# # Add file info to metadata
+# parsed_metadata["filename"] = file.filename
+# parsed_metadata["content_type"] = file.content_type
+
+# # Build orchestrator input
+# orchestrator_input = {
+# "text": "",
+# "audio_data": audio_base64,
+# "metadata": parsed_metadata
+# }
+
+# # Process through orchestrator
+# result = await orchestrator.process(orchestrator_input)
+
+# # Return result
+# return {
+# "response": result.get("response", ""),
+# "voice_data": result.get("voice_data"),
+# "insights": result.get("insights"),
+# "memory_stored": result.get("memory_stored"),
+# "memories_retrieved": result.get("memories_retrieved"),
+# "error": result.get("error")
+# }
+
+# except Exception as e:
+# logger.error(f"File upload error: {e}")
+# raise HTTPException(status_code=500, detail=str(e))
+
+# @router.get("/health")
+# async def voice_health_check(orchestrator: Orchestrator = Depends(get_orchestrator)):
+# """Check voice service health"""
+# try:
+# # Get agent pool stats
+# pool_stats = orchestrator.agent_pool.get_pool_stats()
+
+# return {
+# "status": "healthy",
+# "voice_agent_stats": pool_stats.get("voice", {}),
+# }
+# except Exception as e:
+# return {
+# "status": "unhealthy",
+# "error": str(e)
+# }
\ No newline at end of file
diff --git a/backend/main.py b/backend/main.py
new file mode 100644
index 000000000..317f776b3
--- /dev/null
+++ b/backend/main.py
@@ -0,0 +1,22 @@
+from fastapi import FastAPI
+from pydantic import BaseModel
+from agents.base_agent import ReActAgent
+from fastapi.middleware.cors import CORSMiddleware
+
+app = FastAPI(title="ReAct Agent API")
+agent = ReActAgent()
+
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"], # Or specify your frontend URL
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+class AgentRequest(BaseModel):
+ user_input: str
+
+@app.post("/react")
+async def react_agent(request: AgentRequest):
+ return agent.reason_and_act(request.user_input)
diff --git a/backend/orchestration/__pycache__/agent_pool.cpython-313.pyc b/backend/orchestration/__pycache__/agent_pool.cpython-313.pyc
new file mode 100644
index 000000000..4781d7921
Binary files /dev/null and b/backend/orchestration/__pycache__/agent_pool.cpython-313.pyc differ
diff --git a/backend/orchestration/__pycache__/event_bus.cpython-313.pyc b/backend/orchestration/__pycache__/event_bus.cpython-313.pyc
new file mode 100644
index 000000000..ac4d59efd
Binary files /dev/null and b/backend/orchestration/__pycache__/event_bus.cpython-313.pyc differ
diff --git a/backend/orchestration/__pycache__/orchestrator.cpython-313.pyc b/backend/orchestration/__pycache__/orchestrator.cpython-313.pyc
new file mode 100644
index 000000000..6c7b6b354
Binary files /dev/null and b/backend/orchestration/__pycache__/orchestrator.cpython-313.pyc differ
diff --git a/backend/orchestration/__pycache__/task_queue.cpython-313.pyc b/backend/orchestration/__pycache__/task_queue.cpython-313.pyc
new file mode 100644
index 000000000..8e69731d9
Binary files /dev/null and b/backend/orchestration/__pycache__/task_queue.cpython-313.pyc differ
diff --git a/backend/orchestration/agent_pool.py b/backend/orchestration/agent_pool.py
new file mode 100644
index 000000000..136197c02
--- /dev/null
+++ b/backend/orchestration/agent_pool.py
@@ -0,0 +1,546 @@
+import asyncio
+from typing import Dict, List, Any, Optional, Type
+from dataclasses import dataclass, field
+from datetime import datetime
+from collections import defaultdict
+import logging
+from abc import ABC, abstractmethod
+import uuid
+from langchain.agents import AgentExecutor
+from langchain.memory import ConversationBufferMemory
+from langchain.schema import BaseMessage
+
+# Setup logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class AgentInstance:
+ """Represents an instance of an agent in the pool"""
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ agent_type: str = ""
+ agent: Optional[Any] = None # The actual agent instance
+ status: str = "available" # available, busy, error
+ created_at: datetime = field(default_factory=datetime.now)
+ last_used_at: Optional[datetime] = None
+ usage_count: int = 0
+ current_task_id: Optional[str] = None
+ memory: Optional[ConversationBufferMemory] = None
+
+
+class BasePooledAgent(ABC):
+ """Base class for agents that can be pooled"""
+
+ def __init__(self, agent_id: str):
+ self.agent_id = agent_id
+ self.created_at = datetime.now()
+ self.usage_count = 0
+
+ @abstractmethod
+ async def initialize(self) -> None:
+ """Initialize the agent (load models, etc.)"""
+ pass
+
+ @abstractmethod
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ """Process a request"""
+ pass
+
+ @abstractmethod
+ async def cleanup(self) -> None:
+ """Cleanup resources"""
+ pass
+
+ async def reset(self) -> None:
+ """Reset the agent state between uses"""
+ pass
+
+
+class MockVoiceAgent(BasePooledAgent):
+ """Mock voice agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockVoiceAgent {self.agent_id}")
+ await asyncio.sleep(0.1) # Simulate initialization
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ audio = data.get("audio_data", "")
+ # Simulate processing
+ await asyncio.sleep(0.5)
+ return {
+ "transcription": f"Transcribed audio (agent {self.agent_id})",
+ "confidence": 0.95
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockVoiceAgent {self.agent_id}")
+
+
+class MockPlannerAgent(BasePooledAgent):
+ """Mock planner agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockPlannerAgent {self.agent_id}")
+ self.memory = ConversationBufferMemory()
+ await asyncio.sleep(0.1)
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ transcription = data.get("transcription", "")
+ # Simulate planning
+ await asyncio.sleep(0.3)
+ return {
+ "plan": {
+ "steps": ["analyze", "search", "synthesize", "respond"],
+ "intent": "information_request",
+ "complexity": "medium"
+ }
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockPlannerAgent {self.agent_id}")
+
+ async def reset(self) -> None:
+ """Reset memory between uses"""
+ if self.memory:
+ self.memory.clear()
+
+
+class MockInsightAgent(BasePooledAgent):
+ """Mock insight agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockInsightAgent {self.agent_id}")
+ await asyncio.sleep(0.1)
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ context = data.get("context", {})
+ # Simulate insight generation
+ await asyncio.sleep(0.4)
+ return {
+ "insights": [
+ {"type": "pattern", "description": "User prefers concise answers"},
+ {"type": "recommendation", "description": "Use bullet points"}
+ ]
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockInsightAgent {self.agent_id}")
+
+
+class MockContextAgent(BasePooledAgent):
+ """Mock context agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockContextAgent {self.agent_id}")
+ await asyncio.sleep(0.1)
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ plan = data.get("plan", {})
+ # Simulate context analysis
+ await asyncio.sleep(0.2)
+ return {
+ "context": {
+ "user_profile": {
+ "preferences": ["technical", "detailed"],
+ "history": ["previous_queries"]
+ },
+ "environment": {
+ "time": "afternoon",
+ "location": "office"
+ }
+ }
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockContextAgent {self.agent_id}")
+
+
+class MockMemoryAgent(BasePooledAgent):
+ """Mock memory agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockMemoryAgent {self.agent_id}")
+ self.storage = [] # Simple list for memory storage
+ await asyncio.sleep(0.1)
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ context = data.get("context", {})
+ # Simulate memory storage
+ await asyncio.sleep(0.1)
+ memory_entry = {
+ "timestamp": datetime.now().isoformat(),
+ "interaction": data.get("interaction", ""),
+ "context": context
+ }
+ self.storage.append(memory_entry)
+ return {
+ "memory_id": str(uuid.uuid4()),
+ "stored": True
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockMemoryAgent {self.agent_id}")
+
+
+class MockResponseAgent(BasePooledAgent):
+ """Mock response agent for testing"""
+
+ async def initialize(self) -> None:
+ logger.info(f"Initializing MockResponseAgent {self.agent_id}")
+ await asyncio.sleep(0.1)
+
+ async def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
+ self.usage_count += 1
+ insights = data.get("insights", [])
+ plan = data.get("plan", {})
+ # Simulate response generation
+ await asyncio.sleep(0.3)
+ return {
+ "response": f"Based on your query, I found {len(insights)} insights. "
+ f"The intent was {plan.get('intent', 'unknown')}. "
+ f"This is a test response from agent {self.agent_id}.",
+ "confidence": 0.92
+ }
+
+ async def cleanup(self) -> None:
+ logger.info(f"Cleaning up MockResponseAgent {self.agent_id}")
+
+
+class AgentPool:
+ """Pool manager for LangChain agents"""
+
+ def __init__(self):
+ self.pools: Dict[str, List[AgentInstance]] = defaultdict(list)
+ self.agent_configs: Dict[str, Dict[str, Any]] = {}
+ self.agent_classes: Dict[str, Type[BasePooledAgent]] = {}
+ self.min_pool_size: Dict[str, int] = {}
+ self.max_pool_size: Dict[str, int] = {}
+ self._lock = asyncio.Lock()
+ self._condition = asyncio.Condition()
+ self.statistics: Dict[str, Dict[str, Any]] = defaultdict(lambda: {
+ "total_created": 0,
+ "total_acquired": 0,
+ "total_released": 0,
+ "current_busy": 0,
+ "current_available": 0
+ })
+
+ # Register default agent types
+ self._register_default_agents()
+
+ def _register_default_agents(self):
+ """Register default agent types"""
+ self.register_agent_type(
+ "voice_agent",
+ MockVoiceAgent,
+ min_size=1,
+ max_size=3
+ )
+ self.register_agent_type(
+ "planner_agent",
+ MockPlannerAgent,
+ min_size=2,
+ max_size=5
+ )
+ self.register_agent_type(
+ "context_agent",
+ MockContextAgent,
+ min_size=1,
+ max_size=3
+ )
+ self.register_agent_type(
+ "memory_agent",
+ MockMemoryAgent,
+ min_size=1,
+ max_size=3
+ )
+ self.register_agent_type(
+ "insight_agent",
+ MockInsightAgent,
+ min_size=1,
+ max_size=3
+ )
+ self.register_agent_type(
+ "response_agent",
+ MockResponseAgent,
+ min_size=1,
+ max_size=3
+ )
+
+ def register_agent_type(
+ self,
+ agent_type: str,
+ agent_class: Type[BasePooledAgent],
+ min_size: int = 1,
+ max_size: int = 5,
+ config: Optional[Dict[str, Any]] = None
+ ):
+ """Register a new agent type"""
+ self.agent_classes[agent_type] = agent_class
+ self.min_pool_size[agent_type] = min_size
+ self.max_pool_size[agent_type] = max_size
+ self.agent_configs[agent_type] = config or {}
+ logger.info(f"Registered agent type: {agent_type}")
+
+ async def initialize(self):
+ """Initialize all agent pools"""
+ logger.info("Initializing agent pools...")
+
+ # Create minimum number of agents for each type
+ for agent_type in self.agent_classes:
+ min_size = self.min_pool_size[agent_type]
+ for _ in range(min_size):
+ await self._create_agent(agent_type)
+
+ logger.info("Agent pools initialized")
+
+ async def _create_agent(self, agent_type: str) -> AgentInstance:
+ """Create a new agent instance"""
+ if agent_type not in self.agent_classes:
+ raise ValueError(f"Unknown agent type: {agent_type}")
+
+ agent_class = self.agent_classes[agent_type]
+ agent_id = str(uuid.uuid4())
+
+ # Create the agent
+ agent = agent_class(agent_id)
+ await agent.initialize()
+
+ # Create instance wrapper
+ instance = AgentInstance(
+ id=agent_id,
+ agent_type=agent_type,
+ agent=agent,
+ status="available"
+ )
+
+ # Add to pool
+ async with self._lock:
+ self.pools[agent_type].append(instance)
+ self.statistics[agent_type]["total_created"] += 1
+ self.statistics[agent_type]["current_available"] += 1
+
+ logger.info(f"Created new {agent_type} agent: {agent_id}")
+ return instance
+
+ async def acquire(self, agent_type: str, timeout: float = 30.0) -> Any:
+ """Acquire an agent from the pool"""
+ if agent_type not in self.agent_classes:
+ raise ValueError(f"Unknown agent type: {agent_type}")
+
+ start_time = asyncio.get_event_loop().time()
+
+ async with self._condition:
+ while True:
+ # Check for available agents
+ available_agent = None
+ async with self._lock:
+ pool = self.pools[agent_type]
+ for instance in pool:
+ if instance.status == "available":
+ instance.status = "busy"
+ instance.last_used_at = datetime.now()
+ instance.usage_count += 1
+ available_agent = instance
+ self.statistics[agent_type]["total_acquired"] += 1
+ self.statistics[agent_type]["current_busy"] += 1
+ self.statistics[agent_type]["current_available"] -= 1
+ break
+
+ if available_agent:
+ logger.info(f"Acquired {agent_type} agent: {available_agent.id}")
+ return available_agent.agent
+
+ # Check if we can create a new agent
+ async with self._lock:
+ current_size = len(self.pools[agent_type])
+ max_size = self.max_pool_size[agent_type]
+
+ if current_size < max_size:
+ # Create a new agent
+ instance = await self._create_agent(agent_type)
+ async with self._lock:
+ instance.status = "busy"
+ instance.last_used_at = datetime.now()
+ instance.usage_count += 1
+ self.statistics[agent_type]["total_acquired"] += 1
+ self.statistics[agent_type]["current_busy"] += 1
+ self.statistics[agent_type]["current_available"] -= 1
+ logger.info(f"Created and acquired new {agent_type} agent: {instance.id}")
+ return instance.agent
+
+ # Wait for an agent to become available
+ elapsed = asyncio.get_event_loop().time() - start_time
+ if elapsed >= timeout:
+ raise TimeoutError(f"Timeout waiting for {agent_type} agent")
+
+ try:
+ await asyncio.wait_for(
+ self._condition.wait(),
+ timeout=timeout - elapsed
+ )
+ except asyncio.TimeoutError:
+ raise TimeoutError(f"Timeout waiting for {agent_type} agent")
+
+ async def release(self, agent_type: str, agent: Any) -> None:
+ """Release an agent back to the pool"""
+ if not isinstance(agent, BasePooledAgent):
+ logger.warning(f"Attempted to release non-pooled agent: {agent}")
+ return
+
+ async with self._lock:
+ pool = self.pools[agent_type]
+ for instance in pool:
+ if instance.agent == agent:
+ # Reset the agent
+ await agent.reset()
+
+ # Mark as available
+ instance.status = "available"
+ instance.current_task_id = None
+ self.statistics[agent_type]["total_released"] += 1
+ self.statistics[agent_type]["current_busy"] -= 1
+ self.statistics[agent_type]["current_available"] += 1
+
+ logger.info(f"Released {agent_type} agent: {instance.id}")
+ break
+
+ # Notify waiting tasks
+ async with self._condition:
+ self._condition.notify()
+
+ async def get_pool_stats(self) -> Dict[str, Any]:
+ """Get statistics for all pools"""
+ stats = {}
+ async with self._lock:
+ for agent_type, pool in self.pools.items():
+ type_stats = self.statistics[agent_type].copy()
+ type_stats["pool_size"] = len(pool)
+ type_stats["min_size"] = self.min_pool_size[agent_type]
+ type_stats["max_size"] = self.max_pool_size[agent_type]
+
+ # Calculate average usage
+ if pool:
+ total_usage = sum(instance.usage_count for instance in pool)
+ type_stats["average_usage"] = total_usage / len(pool)
+ else:
+ type_stats["average_usage"] = 0
+
+ stats[agent_type] = type_stats
+
+ return stats
+
+ async def cleanup(self):
+ """Cleanup all agent pools"""
+ logger.info("Cleaning up agent pools...")
+
+ async with self._lock:
+ for agent_type, pool in self.pools.items():
+ for instance in pool:
+ if instance.agent:
+ await instance.agent.cleanup()
+
+ self.pools.clear()
+
+ logger.info("Agent pools cleaned up")
+
+ async def health_check(self) -> Dict[str, Any]:
+ """Perform health check on all pools"""
+ health = {
+ "status": "healthy",
+ "pools": {},
+ "issues": []
+ }
+
+ async with self._lock:
+ for agent_type, pool in self.pools.items():
+ pool_health = {
+ "size": len(pool),
+ "available": sum(1 for i in pool if i.status == "available"),
+ "busy": sum(1 for i in pool if i.status == "busy"),
+ "error": sum(1 for i in pool if i.status == "error")
+ }
+
+ # Check for issues
+ if pool_health["error"] > 0:
+ health["issues"].append(f"{agent_type} has {pool_health['error']} agents in error state")
+
+ if pool_health["size"] < self.min_pool_size[agent_type]:
+ health["issues"].append(f"{agent_type} pool size below minimum")
+
+ health["pools"][agent_type] = pool_health
+
+ if health["issues"]:
+ health["status"] = "degraded"
+
+ return health
+
+
+# Test function
+async def test_agent_pool():
+ """Test the agent pool independently"""
+ pool = AgentPool()
+
+ # Initialize pools
+ await pool.initialize()
+
+ # Get initial stats
+ stats = await pool.get_pool_stats()
+ print(f"Initial pool stats: {stats}")
+
+ # Test acquiring and releasing agents
+ agents = []
+ agent_types = ["voice_agent", "planner_agent", "insight_agent"]
+
+ # Acquire multiple agents
+ for agent_type in agent_types:
+ agent = await pool.acquire(agent_type)
+ agents.append((agent_type, agent))
+ print(f"Acquired {agent_type}")
+
+ # Use the agents
+ for agent_type, agent in agents:
+ result = await agent.process({"test": "data"})
+ print(f"{agent_type} result: {result}")
+
+ # Release agents
+ for agent_type, agent in agents:
+ await pool.release(agent_type, agent)
+ print(f"Released {agent_type}")
+
+ # Test concurrent acquisition
+ async def acquire_and_use(pool, agent_type, duration):
+ agent = await pool.acquire(agent_type)
+ await asyncio.sleep(duration)
+ await pool.release(agent_type, agent)
+
+ # Create concurrent tasks
+ tasks = []
+ for i in range(10):
+ agent_type = agent_types[i % len(agent_types)]
+ task = asyncio.create_task(acquire_and_use(pool, agent_type, 0.5))
+ tasks.append(task)
+
+ # Wait for all tasks
+ await asyncio.gather(*tasks)
+
+ # Final stats
+ final_stats = await pool.get_pool_stats()
+ print(f"Final pool stats: {final_stats}")
+
+ # Health check
+ health = await pool.health_check()
+ print(f"Health check: {health}")
+
+ # Cleanup
+ await pool.cleanup()
+
+
+if __name__ == "__main__":
+ asyncio.run(test_agent_pool())
\ No newline at end of file
diff --git a/backend/orchestration/event_bus.py b/backend/orchestration/event_bus.py
new file mode 100644
index 000000000..b9c403d6b
--- /dev/null
+++ b/backend/orchestration/event_bus.py
@@ -0,0 +1,345 @@
+import asyncio
+from typing import Dict, List, Any, Callable, Optional, Set
+from dataclasses import dataclass, field
+from datetime import datetime
+from collections import defaultdict
+import logging
+import uuid
+from langchain.callbacks.base import AsyncCallbackHandler
+from langchain.schema import BaseMessage
+
+# Setup logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class Event:
+ """Represents an event in the system"""
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ type: str = ""
+ data: Dict[str, Any] = field(default_factory=dict)
+ source: str = ""
+ timestamp: datetime = field(default_factory=datetime.now)
+ metadata: Dict[str, Any] = field(default_factory=dict)
+
+
+class EventBusCallback(AsyncCallbackHandler):
+ """LangChain callback handler that publishes events to the event bus"""
+
+ def __init__(self, event_bus: 'EventBus', source: str = "langchain"):
+ self.event_bus = event_bus
+ self.source = source
+
+ async def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs) -> None:
+ """Called when a chain starts running."""
+ await self.event_bus.publish(Event(
+ type="CHAIN_STARTED",
+ data={"serialized": serialized, "inputs": inputs},
+ source=self.source
+ ))
+
+ async def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None:
+ """Called when a chain ends running."""
+ await self.event_bus.publish(Event(
+ type="CHAIN_COMPLETED",
+ data={"outputs": outputs},
+ source=self.source
+ ))
+
+ async def on_chain_error(self, error: Exception, **kwargs) -> None:
+ """Called when a chain errors."""
+ await self.event_bus.publish(Event(
+ type="CHAIN_ERROR",
+ data={"error": str(error)},
+ source=self.source
+ ))
+
+ async def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs) -> None:
+ """Called when LLM starts running."""
+ await self.event_bus.publish(Event(
+ type="LLM_STARTED",
+ data={"prompts": prompts},
+ source=self.source
+ ))
+
+ async def on_llm_end(self, response: Any, **kwargs) -> None:
+ """Called when LLM ends running."""
+ await self.event_bus.publish(Event(
+ type="LLM_COMPLETED",
+ data={"response": str(response)},
+ source=self.source
+ ))
+
+
+class EventSubscription:
+ """Represents a subscription to events"""
+
+ def __init__(self, event_type: str, handler: Callable, filter_fn: Optional[Callable] = None):
+ self.id = str(uuid.uuid4())
+ self.event_type = event_type
+ self.handler = handler
+ self.filter_fn = filter_fn
+ self.created_at = datetime.now()
+ self.call_count = 0
+
+
+class EventBus:
+ """Event bus implementation for LangChain agents"""
+
+ def __init__(self, max_event_history: int = 1000):
+ self.subscriptions: Dict[str, List[EventSubscription]] = defaultdict(list)
+ self.event_history: List[Event] = []
+ self.max_event_history = max_event_history
+ self.active_handlers: Set[str] = set()
+ self._lock = asyncio.Lock()
+
+ def create_callback_handler(self, source: str = "langchain") -> EventBusCallback:
+ """Create a LangChain callback handler that publishes to this event bus"""
+ return EventBusCallback(self, source)
+
+ async def subscribe(self, event_type: str, handler: Callable, filter_fn: Optional[Callable] = None) -> str:
+ """Subscribe to an event type"""
+ if not asyncio.iscoroutinefunction(handler):
+ raise ValueError("Handler must be an async function")
+
+ subscription = EventSubscription(event_type, handler, filter_fn)
+
+ async with self._lock:
+ self.subscriptions[event_type].append(subscription)
+
+ logger.info(f"Subscribed to {event_type} with handler {handler.__name__} (ID: {subscription.id})")
+ return subscription.id
+
+ async def unsubscribe(self, subscription_id: str) -> bool:
+ """Unsubscribe from events"""
+ async with self._lock:
+ for event_type, subs in self.subscriptions.items():
+ for sub in subs:
+ if sub.id == subscription_id:
+ subs.remove(sub)
+ logger.info(f"Unsubscribed {subscription_id} from {event_type}")
+ return True
+ return False
+
+ async def publish(self, event: Event) -> None:
+ """Publish an event to all subscribers"""
+ if isinstance(event, dict):
+ # Convert dict to Event object for backwards compatibility
+ event = Event(
+ type=event.get("type", "UNKNOWN"),
+ data=event.get("data", {}),
+ source=event.get("source", "unknown")
+ )
+
+ # Add to history
+ async with self._lock:
+ self.event_history.append(event)
+ # Trim history if needed
+ if len(self.event_history) > self.max_event_history:
+ self.event_history = self.event_history[-self.max_event_history:]
+
+ logger.info(f"Publishing event: {event.type} from {event.source}")
+
+ # Get subscribers for this event type
+ subscribers = self.subscriptions.get(event.type, [])
+
+ # Also get wildcard subscribers
+ wildcard_subscribers = self.subscriptions.get("*", [])
+
+ all_subscribers = subscribers + wildcard_subscribers
+
+ # Call all handlers concurrently
+ tasks = []
+ for subscription in all_subscribers:
+ # Apply filter if present
+ if subscription.filter_fn and not subscription.filter_fn(event):
+ continue
+
+ # Create handler task
+ task = asyncio.create_task(self._call_handler(subscription, event))
+ tasks.append(task)
+
+ # Wait for all handlers to complete
+ if tasks:
+ results = await asyncio.gather(*tasks, return_exceptions=True)
+ for i, result in enumerate(results):
+ if isinstance(result, Exception):
+ logger.error(f"Handler error: {result}")
+
+ async def _call_handler(self, subscription: EventSubscription, event: Event) -> None:
+ """Call a single event handler"""
+ handler_id = f"{subscription.id}-{event.id}"
+
+ # Prevent duplicate handling
+ if handler_id in self.active_handlers:
+ return
+
+ self.active_handlers.add(handler_id)
+
+ try:
+ logger.debug(f"Calling handler {subscription.handler.__name__} for event {event.type}")
+ subscription.call_count += 1
+ await subscription.handler(event)
+
+ except Exception as e:
+ logger.error(f"Error in handler {subscription.handler.__name__}: {str(e)}")
+ # Publish error event
+ error_event = Event(
+ type="HANDLER_ERROR",
+ data={
+ "original_event": event.type,
+ "handler": subscription.handler.__name__,
+ "error": str(e)
+ },
+ source="event_bus"
+ )
+ # Don't await to avoid recursion
+ asyncio.create_task(self.publish(error_event))
+
+ finally:
+ self.active_handlers.discard(handler_id)
+
+ async def publish_batch(self, events: List[Event]) -> None:
+ """Publish multiple events"""
+ tasks = [self.publish(event) for event in events]
+ await asyncio.gather(*tasks)
+
+ async def get_event_history(self, event_type: Optional[str] = None, limit: int = 100) -> List[Event]:
+ """Get event history, optionally filtered by type"""
+ async with self._lock:
+ if event_type:
+ filtered = [e for e in self.event_history if e.type == event_type]
+ return filtered[-limit:]
+ else:
+ return self.event_history[-limit:]
+
+ async def get_subscription_stats(self) -> Dict[str, Any]:
+ """Get statistics about subscriptions"""
+ stats = {
+ "total_subscriptions": sum(len(subs) for subs in self.subscriptions.values()),
+ "event_types": list(self.subscriptions.keys()),
+ "subscriptions_by_type": {}
+ }
+
+ for event_type, subs in self.subscriptions.items():
+ stats["subscriptions_by_type"][event_type] = {
+ "count": len(subs),
+ "handlers": [sub.handler.__name__ for sub in subs],
+ "total_calls": sum(sub.call_count for sub in subs)
+ }
+
+ return stats
+
+ async def clear_event_history(self) -> None:
+ """Clear the event history"""
+ async with self._lock:
+ self.event_history.clear()
+
+ def create_typed_publisher(self, event_type: str, source: str) -> Callable:
+ """Create a publisher function for a specific event type"""
+ async def publisher(data: Dict[str, Any], metadata: Optional[Dict[str, Any]] = None):
+ event = Event(
+ type=event_type,
+ data=data,
+ source=source,
+ metadata=metadata or {}
+ )
+ await self.publish(event)
+ return publisher
+
+
+# Global event bus instance for convenience
+_global_event_bus: Optional[EventBus] = None
+
+
+def get_event_bus() -> EventBus:
+ """Get the global event bus instance"""
+ global _global_event_bus
+ if _global_event_bus is None:
+ _global_event_bus = EventBus()
+ return _global_event_bus
+
+
+# Test function
+async def test_event_bus():
+ """Test the event bus independently"""
+ event_bus = EventBus()
+
+ # Test data collection
+ received_events = []
+
+ # Define test handlers
+ async def handler1(event: Event):
+ logger.info(f"Handler1 received: {event.type}")
+ received_events.append(("handler1", event))
+
+ async def handler2(event: Event):
+ logger.info(f"Handler2 received: {event.type}")
+ received_events.append(("handler2", event))
+ await asyncio.sleep(0.1) # Simulate some work
+
+ async def wildcard_handler(event: Event):
+ logger.info(f"Wildcard handler received: {event.type}")
+ received_events.append(("wildcard", event))
+
+ # Subscribe to events
+ sub1 = await event_bus.subscribe("TEST_EVENT", handler1)
+ sub2 = await event_bus.subscribe("TEST_EVENT", handler2)
+ sub3 = await event_bus.subscribe("ANOTHER_EVENT", handler1)
+ sub_wildcard = await event_bus.subscribe("*", wildcard_handler)
+
+ # Publish events
+ await event_bus.publish(Event(
+ type="TEST_EVENT",
+ data={"message": "Hello from test 1"},
+ source="test"
+ ))
+
+ await event_bus.publish(Event(
+ type="ANOTHER_EVENT",
+ data={"message": "Another event"},
+ source="test"
+ ))
+
+ # Test batch publish
+ batch_events = [
+ Event(type="TEST_EVENT", data={"batch": i}, source="batch_test")
+ for i in range(3)
+ ]
+ await event_bus.publish_batch(batch_events)
+
+ # Wait for handlers to complete
+ await asyncio.sleep(0.5)
+
+ # Get statistics
+ stats = await event_bus.get_subscription_stats()
+ print(f"Subscription stats: {stats}")
+
+ # Get event history
+ history = await event_bus.get_event_history()
+ print(f"Event history count: {len(history)}")
+
+ # Test typed publisher
+ test_publisher = event_bus.create_typed_publisher("TYPED_EVENT", "typed_test")
+ await test_publisher({"value": 42})
+
+ # Test LangChain callback
+ callback = event_bus.create_callback_handler("langchain_test")
+ await callback.on_chain_start({}, {"input": "test"})
+ await callback.on_chain_end({"output": "result"})
+
+ # Final statistics
+ print(f"Total events received: {len(received_events)}")
+ for handler_name, event in received_events:
+ print(f" {handler_name}: {event.type}")
+
+ # Unsubscribe
+ await event_bus.unsubscribe(sub1)
+
+ # Clear history
+ await event_bus.clear_event_history()
+
+
+if __name__ == "__main__":
+ asyncio.run(test_event_bus())
\ No newline at end of file
diff --git a/backend/orchestration/orchestrator.py b/backend/orchestration/orchestrator.py
new file mode 100644
index 000000000..4437dfedb
--- /dev/null
+++ b/backend/orchestration/orchestrator.py
@@ -0,0 +1,487 @@
+from typing import Dict, List, Any, Optional, TypedDict, Annotated, Sequence
+from langgraph.graph import StateGraph, END
+from langchain.schema import BaseMessage, HumanMessage, AIMessage
+from langchain.callbacks.manager import CallbackManagerForChainRun
+from dataclasses import dataclass
+from enum import Enum
+import logging
+import json
+import uuid
+from datetime import datetime
+
+# Setup logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+class AgentState(TypedDict):
+ """State passed between nodes in the graph"""
+ messages: Sequence[BaseMessage]
+ current_agent: str
+ task_data: Dict[str, Any]
+ workflow_id: str
+ user_id: str
+ context: Dict[str, Any]
+ plan: Dict[str, Any]
+ memories: List[Dict[str, Any]]
+ insights: List[Dict[str, Any]]
+ response: str
+ error: Optional[str]
+ retry_count: int
+
+
+class EventType(Enum):
+ """Enumeration of all possible event types in the system"""
+ TRANSCRIPTION_COMPLETE = "transcription_complete"
+ PLAN_CREATED = "plan_created"
+ CONTEXT_ANALYZED = "context_analyzed"
+ MEMORY_STORED = "memory_stored"
+ INSIGHT_GENERATED = "insight_generated"
+ RESPONSE_READY = "response_ready"
+ VISION_ANALYZED = "vision_analyzed"
+
+
+class Orchestrator:
+ """Main orchestrator that coordinates all agents using LangGraph"""
+
+ def __init__(self, agent_pool=None, event_bus=None, task_queue=None):
+ self.agent_pool = agent_pool
+ self.event_bus = event_bus
+ self.task_queue = task_queue
+ self.graph = self._build_graph()
+ self.compiled_graph = self.graph.compile()
+
+ def _build_graph(self) -> StateGraph:
+ """Build the LangGraph workflow"""
+ # Define the graph
+ workflow = StateGraph(AgentState)
+
+ # Add nodes for each agent
+ workflow.add_node("voice_agent", self._voice_agent_node)
+ workflow.add_node("planner_agent", self._planner_agent_node)
+ workflow.add_node("context_agent", self._context_agent_node)
+ workflow.add_node("memory_agent", self._memory_agent_node)
+ workflow.add_node("insight_agent", self._insight_agent_node)
+ workflow.add_node("response_agent", self._response_agent_node)
+ workflow.add_node("error_handler", self._error_handler_node)
+
+ # Set the entry point
+ workflow.set_entry_point("voice_agent")
+
+ # Add edges
+ workflow.add_edge("voice_agent", "planner_agent")
+ workflow.add_edge("planner_agent", "context_agent")
+ workflow.add_edge("context_agent", "memory_agent")
+ workflow.add_edge("memory_agent", "insight_agent")
+ workflow.add_edge("insight_agent", "response_agent")
+ workflow.add_edge("response_agent", END)
+
+ # Add conditional edges for error handling
+ workflow.add_conditional_edges(
+ "voice_agent",
+ self._check_error,
+ {
+ "error": "error_handler",
+ "continue": "planner_agent"
+ }
+ )
+
+ workflow.add_conditional_edges(
+ "planner_agent",
+ self._check_error,
+ {
+ "error": "error_handler",
+ "continue": "context_agent"
+ }
+ )
+
+ return workflow
+
+ def _check_error(self, state: AgentState) -> str:
+ """Check if there's an error in the state"""
+ if state.get("error"):
+ return "error"
+ return "continue"
+
+ async def _voice_agent_node(self, state: AgentState) -> AgentState:
+ """Process voice input"""
+ try:
+ logger.info("Processing voice input...")
+
+ # Get agent from pool if available
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("voice_agent")
+
+ # Process the voice data
+ audio_data = state["task_data"].get("audio_data", "")
+
+ # Simulate processing (in real implementation, use actual voice agent)
+ transcription = f"Transcribed: {state['task_data'].get('text', 'Hello, this is a test')}"
+
+ # Update state
+ state["messages"].append(HumanMessage(content=transcription))
+ state["task_data"]["transcription"] = transcription
+ state["current_agent"] = "voice_agent"
+
+ # Publish event if event bus is available
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.TRANSCRIPTION_COMPLETE,
+ "data": {
+ "transcription": transcription,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent back to pool
+ if self.agent_pool and agent:
+ await self.agent_pool.release("voice_agent", agent)
+
+ logger.info(f"Voice transcription complete: {transcription[:50]}...")
+
+ except Exception as e:
+ logger.error(f"Error in voice agent: {str(e)}")
+ state["error"] = str(e)
+ state["retry_count"] = state.get("retry_count", 0) + 1
+
+ return state
+
+ async def _planner_agent_node(self, state: AgentState) -> AgentState:
+ """Create a plan based on the transcription"""
+ try:
+ logger.info("Creating plan...")
+
+ # Get agent from pool
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("planner_agent")
+
+ transcription = state["task_data"].get("transcription", "")
+
+ # Simulate planning (in real implementation, use actual planner agent)
+ plan = {
+ "steps": [
+ "Analyze user intent",
+ "Gather relevant context",
+ "Generate insights",
+ "Formulate response"
+ ],
+ "intent": "user_query",
+ "priority": "high"
+ }
+
+ # Update state
+ state["plan"] = plan
+ state["current_agent"] = "planner_agent"
+ state["messages"].append(AIMessage(content=f"Plan created: {json.dumps(plan)}"))
+
+ # Publish event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.PLAN_CREATED,
+ "data": {
+ "plan": plan,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent
+ if self.agent_pool and agent:
+ await self.agent_pool.release("planner_agent", agent)
+
+ logger.info("Plan created successfully")
+
+ except Exception as e:
+ logger.error(f"Error in planner agent: {str(e)}")
+ state["error"] = str(e)
+
+ return state
+
+ async def _context_agent_node(self, state: AgentState) -> AgentState:
+ """Analyze context based on the plan"""
+ try:
+ logger.info("Analyzing context...")
+
+ # Get agent from pool
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("context_agent")
+
+ plan = state.get("plan", {})
+
+ # Simulate context analysis
+ context = {
+ "user_profile": {
+ "preferences": ["technical", "detailed"],
+ "history": ["previous_queries"]
+ },
+ "environment": {
+ "time": "afternoon",
+ "location": "office"
+ },
+ "relevant_data": ["data1", "data2"]
+ }
+
+ # Update state
+ state["context"] = context
+ state["current_agent"] = "context_agent"
+ state["messages"].append(AIMessage(content=f"Context analyzed: {json.dumps(context)}"))
+
+ # Publish event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.CONTEXT_ANALYZED,
+ "data": {
+ "context": context,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent
+ if self.agent_pool and agent:
+ await self.agent_pool.release("context_agent", agent)
+
+ logger.info("Context analysis complete")
+
+ except Exception as e:
+ logger.error(f"Error in context agent: {str(e)}")
+ state["error"] = str(e)
+
+ return state
+
+ async def _memory_agent_node(self, state: AgentState) -> AgentState:
+ """Store relevant information in memory"""
+ try:
+ logger.info("Storing memory...")
+
+ # Get agent from pool
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("memory_agent")
+
+ context = state.get("context", {})
+
+ # Simulate memory storage
+ memory_entry = {
+ "timestamp": "2024-01-01T00:00:00",
+ "interaction": state["task_data"].get("transcription", ""),
+ "context": context,
+ "tags": ["voice", "query"]
+ }
+
+ # Update state
+ state["memories"] = [memory_entry]
+ state["current_agent"] = "memory_agent"
+ state["messages"].append(AIMessage(content="Memory stored successfully"))
+
+ # Publish event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.MEMORY_STORED,
+ "data": {
+ "memory": memory_entry,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent
+ if self.agent_pool and agent:
+ await self.agent_pool.release("memory_agent", agent)
+
+ logger.info("Memory stored successfully")
+
+ except Exception as e:
+ logger.error(f"Error in memory agent: {str(e)}")
+ state["error"] = str(e)
+
+ return state
+
+ async def _insight_agent_node(self, state: AgentState) -> AgentState:
+ """Generate insights based on context and memories"""
+ try:
+ logger.info("Generating insights...")
+
+ # Get agent from pool
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("insight_agent")
+
+ context = state.get("context", {})
+ memories = state.get("memories", [])
+
+ # Simulate insight generation
+ insights = [
+ {
+ "type": "pattern",
+ "description": "User frequently asks technical questions",
+ "confidence": 0.85
+ },
+ {
+ "type": "recommendation",
+ "description": "Provide detailed technical explanations",
+ "confidence": 0.90
+ }
+ ]
+
+ # Update state
+ state["insights"] = insights
+ state["current_agent"] = "insight_agent"
+ state["messages"].append(AIMessage(content=f"Insights generated: {len(insights)} insights"))
+
+ # Publish event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.INSIGHT_GENERATED,
+ "data": {
+ "insights": insights,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent
+ if self.agent_pool and agent:
+ await self.agent_pool.release("insight_agent", agent)
+
+ logger.info(f"Generated {len(insights)} insights")
+
+ except Exception as e:
+ logger.error(f"Error in insight agent: {str(e)}")
+ state["error"] = str(e)
+
+ return state
+
+ async def _response_agent_node(self, state: AgentState) -> AgentState:
+ """Generate final response based on all previous processing"""
+ try:
+ logger.info("Generating response...")
+
+ # Get agent from pool
+ agent = None
+ if self.agent_pool:
+ agent = await self.agent_pool.acquire("response_agent")
+
+ plan = state.get("plan", {})
+ context = state.get("context", {})
+ insights = state.get("insights", [])
+
+ if agent:
+ # Use the actual agent
+ result = await agent.process({
+ "plan": plan,
+ "context": context,
+ "insights": insights,
+ "transcription": state['task_data'].get('transcription', '')
+ })
+ response = result.get("response", "No response generated")
+ else:
+ # Fallback response
+ response = f"""Based on your query: {state['task_data'].get('transcription', '')}
+
+I've analyzed the context and generated the following response:
+- Intent: {plan.get('intent', 'unknown')}
+- Relevant insights: {len(insights)} insights found
+- Personalized based on your preferences
+
+This is a sample response that would be generated by the actual response agent."""
+
+ # Update state
+ state["response"] = response
+ state["current_agent"] = "response_agent"
+ state["messages"].append(AIMessage(content=response))
+
+ # Publish event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": EventType.RESPONSE_READY,
+ "data": {
+ "response": response,
+ "user_id": state["user_id"]
+ }
+ })
+
+ # Release agent
+ if self.agent_pool and agent:
+ await self.agent_pool.release("response_agent", agent)
+
+ logger.info("Response generated successfully")
+
+ except Exception as e:
+ logger.error(f"Error in response agent: {str(e)}")
+ state["error"] = str(e)
+
+ return state
+
+ async def _error_handler_node(self, state: AgentState) -> AgentState:
+ """Handle errors in the workflow"""
+ error = state.get("error", "Unknown error")
+ retry_count = state.get("retry_count", 0)
+
+ logger.error(f"Error in workflow: {error}, retry count: {retry_count}")
+
+ if retry_count < 3:
+ # Clear error and retry
+ state["error"] = None
+ logger.info(f"Retrying workflow, attempt {retry_count + 1}")
+ else:
+ # Max retries reached, set final error response
+ state["response"] = f"I apologize, but I encountered an error processing your request: {error}"
+
+ return state
+
+ async def process_request(self, request_data: Dict[str, Any]) -> Dict[str, Any]:
+ """Process a request through the orchestration workflow"""
+ initial_state = {
+ "messages": [],
+ "current_agent": "orchestrator",
+ "task_data": request_data,
+ "workflow_id": request_data.get("workflow_id", "default"),
+ "user_id": request_data.get("user_id", "default_user"),
+ "context": {},
+ "plan": {},
+ "memories": [],
+ "insights": [],
+ "response": "",
+ "error": None,
+ "retry_count": 0
+ }
+
+ try:
+ # Run the graph
+ final_state = await self.compiled_graph.ainvoke(initial_state)
+
+ return {
+ "success": True,
+ "response": final_state.get("response", ""),
+ "workflow_id": final_state.get("workflow_id"),
+ "insights": final_state.get("insights", [])
+ }
+
+ except Exception as e:
+ logger.error(f"Error in orchestration: {str(e)}")
+ return {
+ "success": False,
+ "error": str(e),
+ "workflow_id": initial_state.get("workflow_id")
+ }
+
+
+# Test function
+async def test_orchestrator():
+ """Test the orchestrator independently"""
+ orchestrator = Orchestrator()
+
+ test_request = {
+ "audio_data": "test_audio_base64",
+ "text": "What's the weather like today?",
+ "user_id": "test_user",
+ "workflow_id": "test_workflow"
+ }
+
+ result = await orchestrator.process_request(test_request)
+ print(f"Test result: {json.dumps(result, indent=2)}")
+
+
+if __name__ == "__main__":
+ import asyncio
+ asyncio.run(test_orchestrator())
\ No newline at end of file
diff --git a/backend/orchestration/task_queue.py b/backend/orchestration/task_queue.py
new file mode 100644
index 000000000..4beb029c2
--- /dev/null
+++ b/backend/orchestration/task_queue.py
@@ -0,0 +1,361 @@
+import asyncio
+from typing import Dict, List, Any, Optional, Callable
+from dataclasses import dataclass, field
+from datetime import datetime
+from collections import deque
+from enum import Enum
+import logging
+import uuid
+from langchain.callbacks.base import AsyncCallbackHandler
+from langchain.schema import LLMResult
+
+# Setup logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+class TaskStatus(Enum):
+ PENDING = "pending"
+ RUNNING = "running"
+ COMPLETED = "completed"
+ FAILED = "failed"
+ RETRYING = "retrying"
+
+
+@dataclass
+class Task:
+ """Represents a task in the queue"""
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ type: str = ""
+ data: Dict[str, Any] = field(default_factory=dict)
+ priority: int = 1
+ created_at: datetime = field(default_factory=datetime.now)
+ started_at: Optional[datetime] = None
+ completed_at: Optional[datetime] = None
+ status: TaskStatus = TaskStatus.PENDING
+ retries: int = 0
+ max_retries: int = 3
+ result: Optional[Any] = None
+ error: Optional[str] = None
+ callback_handler: Optional[AsyncCallbackHandler] = None
+
+
+class TaskQueueCallback(AsyncCallbackHandler):
+ """Callback handler for task execution tracking"""
+
+ def __init__(self, task_queue: 'TaskQueue', task: Task):
+ self.task_queue = task_queue
+ self.task = task
+
+ async def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs) -> None:
+ """Called when a chain starts running."""
+ logger.info(f"Task {self.task.id} chain started")
+ self.task.status = TaskStatus.RUNNING
+ self.task.started_at = datetime.now()
+
+ async def on_chain_end(self, outputs: Dict[str, Any], **kwargs) -> None:
+ """Called when a chain ends running."""
+ logger.info(f"Task {self.task.id} chain completed")
+ self.task.status = TaskStatus.COMPLETED
+ self.task.completed_at = datetime.now()
+ self.task.result = outputs
+
+ async def on_chain_error(self, error: Exception, **kwargs) -> None:
+ """Called when a chain errors."""
+ logger.error(f"Task {self.task.id} chain error: {str(error)}")
+ self.task.status = TaskStatus.FAILED
+ self.task.error = str(error)
+
+ # Handle retry logic
+ if self.task.retries < self.task.max_retries:
+ self.task.retries += 1
+ self.task.status = TaskStatus.RETRYING
+ await self.task_queue.enqueue(self.task)
+
+
+class PriorityQueue:
+ """Simple priority queue implementation"""
+
+ def __init__(self):
+ self.queues = {1: deque(), 2: deque(), 3: deque()} # 3 priority levels
+ self.lock = asyncio.Lock()
+
+ async def put(self, task: Task):
+ """Add a task to the queue"""
+ async with self.lock:
+ priority = min(max(task.priority, 1), 3) # Clamp between 1-3
+ self.queues[priority].append(task)
+
+ async def get(self) -> Optional[Task]:
+ """Get the highest priority task"""
+ async with self.lock:
+ # Check from highest priority (3) to lowest (1)
+ for priority in [3, 2, 1]:
+ if self.queues[priority]:
+ return self.queues[priority].popleft()
+ return None
+
+ async def size(self) -> int:
+ """Get total queue size"""
+ async with self.lock:
+ return sum(len(q) for q in self.queues.values())
+
+ def empty(self) -> bool:
+ """Check if queue is empty"""
+ return all(len(q) == 0 for q in self.queues.values())
+
+
+class TaskQueue:
+ """Task queue implementation for LangChain agents"""
+
+ def __init__(self, max_concurrent_tasks: int = 5):
+ self.queue = PriorityQueue()
+ self.max_concurrent_tasks = max_concurrent_tasks
+ self.running_tasks: Dict[str, Task] = {}
+ self.completed_tasks: Dict[str, Task] = {}
+ self.workers: List[asyncio.Task] = []
+ self.is_running = False
+ self.event_bus = None # Will be injected
+ self.agent_pool = None # Will be injected
+
+ async def start(self, num_workers: int = 3):
+ """Start the task queue workers"""
+ self.is_running = True
+ logger.info(f"Starting task queue with {num_workers} workers")
+
+ # Create worker tasks
+ for i in range(num_workers):
+ worker = asyncio.create_task(self._worker(f"worker-{i}"))
+ self.workers.append(worker)
+
+ async def stop(self):
+ """Stop the task queue"""
+ logger.info("Stopping task queue...")
+ self.is_running = False
+
+ # Cancel all workers
+ for worker in self.workers:
+ worker.cancel()
+
+ # Wait for workers to finish
+ await asyncio.gather(*self.workers, return_exceptions=True)
+ self.workers.clear()
+
+ async def enqueue(self, task: Task) -> str:
+ """Add a task to the queue"""
+ if not isinstance(task, Task):
+ raise ValueError("task must be an instance of Task")
+
+ # Set callback handler
+ task.callback_handler = TaskQueueCallback(self, task)
+
+ await self.queue.put(task)
+ logger.info(f"Enqueued task {task.id} of type {task.type} with priority {task.priority}")
+
+ # Emit event if event bus is available
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": "TASK_CREATED",
+ "data": {
+ "task_id": task.id,
+ "task_type": task.type,
+ "priority": task.priority
+ }
+ })
+
+ return task.id
+
+ async def get_task_status(self, task_id: str) -> Optional[Dict[str, Any]]:
+ """Get the status of a task"""
+ # Check running tasks
+ if task_id in self.running_tasks:
+ task = self.running_tasks[task_id]
+ return {
+ "id": task.id,
+ "status": task.status.value,
+ "type": task.type,
+ "started_at": task.started_at.isoformat() if task.started_at else None
+ }
+
+ # Check completed tasks
+ if task_id in self.completed_tasks:
+ task = self.completed_tasks[task_id]
+ return {
+ "id": task.id,
+ "status": task.status.value,
+ "type": task.type,
+ "result": task.result,
+ "completed_at": task.completed_at.isoformat() if task.completed_at else None
+ }
+
+ return None
+
+ async def _worker(self, worker_id: str):
+ """Worker coroutine that processes tasks from the queue"""
+ logger.info(f"Worker {worker_id} started")
+
+ while self.is_running:
+ try:
+ # Check if we can run more tasks
+ if len(self.running_tasks) >= self.max_concurrent_tasks:
+ await asyncio.sleep(0.1)
+ continue
+
+ # Get a task from the queue
+ task = await self.queue.get()
+ if not task:
+ await asyncio.sleep(0.1)
+ continue
+
+ # Process the task
+ logger.info(f"Worker {worker_id} processing task {task.id}")
+ self.running_tasks[task.id] = task
+
+ try:
+ # Execute the task based on its type
+ result = await self._execute_task(task)
+ task.result = result
+ task.status = TaskStatus.COMPLETED
+ task.completed_at = datetime.now()
+
+ # Move to completed
+ self.completed_tasks[task.id] = task
+ del self.running_tasks[task.id]
+
+ # Emit completion event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": "TASK_COMPLETED",
+ "data": {
+ "task_id": task.id,
+ "task_type": task.type,
+ "result": result
+ }
+ })
+
+ except Exception as e:
+ logger.error(f"Task {task.id} failed: {str(e)}")
+ task.error = str(e)
+ task.status = TaskStatus.FAILED
+
+ # Handle retry
+ if task.retries < task.max_retries:
+ task.retries += 1
+ task.status = TaskStatus.RETRYING
+ await self.enqueue(task)
+ logger.info(f"Retrying task {task.id} (attempt {task.retries})")
+ else:
+ # Move to completed with failed status
+ self.completed_tasks[task.id] = task
+
+ del self.running_tasks[task.id]
+
+ # Emit failure event
+ if self.event_bus:
+ await self.event_bus.publish({
+ "type": "TASK_FAILED",
+ "data": {
+ "task_id": task.id,
+ "task_type": task.type,
+ "error": str(e),
+ "retries": task.retries
+ }
+ })
+
+ except asyncio.CancelledError:
+ break
+ except Exception as e:
+ logger.error(f"Worker {worker_id} error: {str(e)}")
+ await asyncio.sleep(1)
+
+ logger.info(f"Worker {worker_id} stopped")
+
+ async def _execute_task(self, task: Task) -> Any:
+ """Execute a task based on its type"""
+ logger.info(f"Executing task {task.id} of type {task.type}")
+
+ # Simulate task execution based on type
+ # In real implementation, this would call the appropriate agent
+
+ if task.type == "voice_agent":
+ # Simulate voice processing
+ await asyncio.sleep(1)
+ return {"transcription": "Simulated transcription"}
+
+ elif task.type == "planner_agent":
+ # Simulate planning
+ await asyncio.sleep(0.5)
+ return {"plan": {"steps": ["step1", "step2"]}}
+
+ elif task.type == "context_agent":
+ # Simulate context analysis
+ await asyncio.sleep(0.3)
+ return {"context": {"user_intent": "query"}}
+
+ elif task.type == "memory_agent":
+ # Simulate memory storage
+ await asyncio.sleep(0.2)
+ return {"memory_id": str(uuid.uuid4())}
+
+ elif task.type == "insight_agent":
+ # Simulate insight generation
+ await asyncio.sleep(0.8)
+ return {"insights": ["insight1", "insight2"]}
+
+ elif task.type == "response_agent":
+ # Simulate response generation
+ await asyncio.sleep(0.5)
+ return {"response": "This is a simulated response"}
+
+ else:
+ raise ValueError(f"Unknown task type: {task.type}")
+
+ async def get_queue_stats(self) -> Dict[str, Any]:
+ """Get queue statistics"""
+ return {
+ "queue_size": await self.queue.size(),
+ "running_tasks": len(self.running_tasks),
+ "completed_tasks": len(self.completed_tasks),
+ "max_concurrent_tasks": self.max_concurrent_tasks,
+ "is_running": self.is_running,
+ "num_workers": len(self.workers)
+ }
+
+
+# Test function
+async def test_task_queue():
+ """Test the task queue independently"""
+ queue = TaskQueue(max_concurrent_tasks=3)
+
+ # Start the queue
+ await queue.start(num_workers=2)
+
+ # Create test tasks
+ tasks = []
+ for i in range(5):
+ task = Task(
+ type=["voice_agent", "planner_agent", "insight_agent"][i % 3],
+ data={"test_data": f"test_{i}"},
+ priority=(i % 3) + 1
+ )
+ task_id = await queue.enqueue(task)
+ tasks.append(task_id)
+
+ # Wait a bit for processing
+ await asyncio.sleep(3)
+
+ # Check task statuses
+ for task_id in tasks:
+ status = await queue.get_task_status(task_id)
+ print(f"Task {task_id}: {status}")
+
+ # Get queue stats
+ stats = await queue.get_queue_stats()
+ print(f"Queue stats: {stats}")
+
+ # Stop the queue
+ await queue.stop()
+
+
+if __name__ == "__main__":
+ asyncio.run(test_task_queue())
\ No newline at end of file
diff --git a/backend/orchestration/test_orchestrator.py b/backend/orchestration/test_orchestrator.py
new file mode 100644
index 000000000..8447541af
--- /dev/null
+++ b/backend/orchestration/test_orchestrator.py
@@ -0,0 +1,398 @@
+import asyncio
+import logging
+from typing import Dict, Any
+import json
+from datetime import datetime
+
+# Import our orchestration components
+# Note: In real usage, these would be imported from the actual files
+# from orchestrator import Orchestrator
+# from event_bus import EventBus
+# from agent_pool import AgentPool
+# from task_queue import TaskQueue
+
+# For testing purposes, we'll create a simplified integration test
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+
+class IntegrationTest:
+ """Integration test for the orchestration system"""
+
+ def __init__(self):
+ self.event_bus = None
+ self.agent_pool = None
+ self.task_queue = None
+ self.orchestrator = None
+ self.test_results = {
+ "events_published": 0,
+ "tasks_completed": 0,
+ "agents_used": set(),
+ "errors": []
+ }
+ # Import these once at class level
+ self.Event = None
+ self.Task = None
+ self.EventType = None
+
+ async def setup(self):
+ """Setup all components"""
+ logger.info("Setting up orchestration components...")
+
+ # Import components (adjust the import based on your file structure)
+ try:
+ from event_bus import EventBus, Event
+ from agent_pool import AgentPool
+ from task_queue import TaskQueue, Task
+ from orchestrator import Orchestrator, EventType
+ except ImportError:
+ # If running from the orchestration directory
+ import sys
+ import os
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from event_bus import EventBus, Event
+ from agent_pool import AgentPool
+ from task_queue import TaskQueue, Task
+ from orchestrator import Orchestrator, EventType
+
+ # Store these as class attributes for use in test methods
+ self.Event = Event
+ self.Task = Task
+ self.EventType = EventType
+
+ # Create components
+ self.event_bus = EventBus()
+ self.agent_pool = AgentPool()
+ self.task_queue = TaskQueue(max_concurrent_tasks=5)
+
+ # Initialize agent pool
+ await self.agent_pool.initialize()
+
+ # Start task queue
+ await self.task_queue.start(num_workers=3)
+
+ # Create orchestrator and inject dependencies
+ self.orchestrator = Orchestrator(
+ agent_pool=self.agent_pool,
+ event_bus=self.event_bus,
+ task_queue=self.task_queue
+ )
+
+ # Setup event tracking
+ await self._setup_event_tracking()
+
+ logger.info("Setup complete")
+
+ async def _setup_event_tracking(self):
+ """Setup event tracking for test metrics"""
+ # Track all events
+ async def track_event(event):
+ self.test_results["events_published"] += 1
+ logger.info(f"Event tracked: {event.type}")
+
+ await self.event_bus.subscribe("*", track_event)
+
+ # Track task completions
+ async def track_task_complete(event):
+ self.test_results["tasks_completed"] += 1
+ task_type = event.data.get("task_type", "unknown")
+ self.test_results["agents_used"].add(task_type)
+
+ await self.event_bus.subscribe("TASK_COMPLETED", track_task_complete)
+
+ async def test_simple_workflow(self):
+ """Test a simple voice processing workflow"""
+ logger.info("\n=== Testing Simple Workflow ===")
+
+ # For testing, let's directly test the graph execution
+ # since the full orchestrator might need actual agent implementations
+ try:
+ # First, let's test if we can create a simple request
+ request = {
+ "audio_data": "base64_encoded_audio_data",
+ "text": "What is the weather forecast for tomorrow?",
+ "user_id": "test_user_001",
+ "workflow_id": "test_workflow_001"
+ }
+
+ logger.info(f"Testing with request: {request}")
+
+ # Since we're using mock agents, let's test the components separately first
+ # Test task queue directly
+ test_task = self.Task(
+ type="voice_agent",
+ data=request,
+ priority=3
+ )
+
+ task_id = await self.task_queue.enqueue(test_task)
+ logger.info(f"Enqueued test task: {task_id}")
+
+ # Give it time to process
+ await asyncio.sleep(2)
+
+ # Check task status
+ status = await self.task_queue.get_task_status(task_id)
+ logger.info(f"Task status: {status}")
+
+ # Now test the orchestrator
+ result = await self.orchestrator.process_request(request)
+
+ logger.info(f"Workflow result: {json.dumps(result, indent=2)}")
+
+ # More flexible assertions
+ assert "success" in result, "Result should have success field"
+ assert "workflow_id" in result, "Result should have workflow_id"
+
+ # Log what we got
+ if result.get("success"):
+ logger.info("Workflow completed successfully")
+ else:
+ logger.warning(f"Workflow failed: {result.get('error')}")
+
+ return result
+
+ except Exception as e:
+ logger.error(f"Error in simple workflow test: {str(e)}")
+ # Don't fail the entire test suite
+ return {"success": False, "error": str(e)}
+
+ async def test_concurrent_workflows(self):
+ """Test multiple concurrent workflows"""
+ logger.info("\n=== Testing Concurrent Workflows ===")
+
+ try:
+ # Create multiple test requests
+ requests = []
+ for i in range(5):
+ request = {
+ "audio_data": f"audio_data_{i}",
+ "text": f"Test query {i}",
+ "user_id": f"test_user_{i:03d}",
+ "workflow_id": f"test_workflow_{i:03d}"
+ }
+ requests.append(request)
+
+ # Process all requests concurrently
+ tasks = [self.orchestrator.process_request(req) for req in requests]
+ results = await asyncio.gather(*tasks, return_exceptions=True)
+
+ # Check results
+ successful = 0
+ failed = 0
+
+ for i, result in enumerate(results):
+ if isinstance(result, Exception):
+ logger.error(f"Workflow {i} failed with exception: {result}")
+ failed += 1
+ elif isinstance(result, dict) and result.get("success"):
+ logger.info(f"Workflow {i} completed successfully")
+ successful += 1
+ else:
+ logger.warning(f"Workflow {i} returned: {result}")
+ failed += 1
+
+ logger.info(f"Concurrent test results: {successful} successful, {failed} failed")
+
+ return results
+
+ except Exception as e:
+ logger.error(f"Error in concurrent workflow test: {str(e)}")
+ return []
+
+ async def test_task_queue_operations(self):
+ """Test task queue operations directly"""
+ logger.info("\n=== Testing Task Queue Operations ===")
+
+ # Import Task from the module we already imported
+ from task_queue import Task
+
+ # Create test tasks
+ tasks = []
+ for agent_type in ["voice_agent", "planner_agent", "insight_agent"]:
+ task = Task(
+ type=agent_type,
+ data={"test": f"data_for_{agent_type}"},
+ priority=2
+ )
+ task_id = await self.task_queue.enqueue(task)
+ tasks.append(task_id)
+ logger.info(f"Enqueued task {task_id} for {agent_type}")
+
+ # Wait for processing
+ await asyncio.sleep(2)
+
+ # Check task statuses
+ for task_id in tasks:
+ status = await self.task_queue.get_task_status(task_id)
+ logger.info(f"Task {task_id} status: {status}")
+
+ # Get queue stats
+ stats = await self.task_queue.get_queue_stats()
+ logger.info(f"Queue stats: {json.dumps(stats, indent=2)}")
+
+ return stats
+
+ async def test_event_bus_operations(self):
+ """Test event bus operations directly"""
+ logger.info("\n=== Testing Event Bus Operations ===")
+
+ # Test custom event handler
+ custom_events = []
+
+ async def custom_handler(event):
+ custom_events.append(event)
+ logger.info(f"Custom handler received: {event.type}")
+
+ # Subscribe to custom events
+ await self.event_bus.subscribe("CUSTOM_EVENT", custom_handler)
+
+ # Publish custom events
+ for i in range(3):
+ event = self.Event(
+ type="CUSTOM_EVENT",
+ data={"index": i, "message": f"Custom event {i}"},
+ source="test"
+ )
+ await self.event_bus.publish(event)
+
+ # Wait for handlers
+ await asyncio.sleep(0.5)
+
+ # Verify events received
+ assert len(custom_events) == 3, "Should receive all custom events"
+
+ # Get event history
+ history = await self.event_bus.get_event_history("CUSTOM_EVENT")
+ logger.info(f"Custom event history: {len(history)} events")
+
+ # Get subscription stats
+ stats = await self.event_bus.get_subscription_stats()
+ logger.info(f"Event bus stats: {json.dumps(stats, indent=2)}")
+
+ return stats
+
+ async def test_agent_pool_operations(self):
+ """Test agent pool operations directly"""
+ logger.info("\n=== Testing Agent Pool Operations ===")
+
+ # Test acquiring and releasing agents
+ acquired_agents = []
+
+ for agent_type in ["voice_agent", "planner_agent"]:
+ agent = await self.agent_pool.acquire(agent_type)
+ acquired_agents.append((agent_type, agent))
+ logger.info(f"Acquired {agent_type}")
+
+ # Use the agent
+ result = await agent.process({"test": "data"})
+ logger.info(f"{agent_type} processed: {result}")
+
+ # Release agents
+ for agent_type, agent in acquired_agents:
+ await self.agent_pool.release(agent_type, agent)
+ logger.info(f"Released {agent_type}")
+
+ # Get pool stats
+ stats = await self.agent_pool.get_pool_stats()
+ logger.info(f"Pool stats: {json.dumps(stats, indent=2)}")
+
+ # Health check
+ health = await self.agent_pool.health_check()
+ logger.info(f"Pool health: {json.dumps(health, indent=2)}")
+
+ return stats
+
+ async def test_error_handling(self):
+ """Test error handling in the system"""
+ logger.info("\n=== Testing Error Handling ===")
+
+ # Test with invalid request
+ invalid_request = {
+ "invalid_field": "test",
+ "user_id": "error_test_user"
+ }
+
+ result = await self.orchestrator.process_request(invalid_request)
+ logger.info(f"Error handling result: {json.dumps(result, indent=2)}")
+
+ # Test task retry
+ error_task = self.Task(
+ type="error_agent", # Non-existent agent type
+ data={"test": "error"},
+ max_retries=2
+ )
+
+ task_id = await self.task_queue.enqueue(error_task)
+ await asyncio.sleep(2)
+
+ status = await self.task_queue.get_task_status(task_id)
+ logger.info(f"Error task status: {status}")
+
+ return result
+
+ async def cleanup(self):
+ """Cleanup all components"""
+ logger.info("\n=== Cleaning up ===")
+
+ # Stop task queue
+ if self.task_queue:
+ await self.task_queue.stop()
+
+ # Cleanup agent pool
+ if self.agent_pool:
+ await self.agent_pool.cleanup()
+
+ # Clear event history
+ if self.event_bus:
+ await self.event_bus.clear_event_history()
+
+ logger.info("Cleanup complete")
+
+ async def run_all_tests(self):
+ """Run all integration tests"""
+ logger.info("Starting Orchestration Integration Tests")
+ logger.info("=" * 50)
+
+ try:
+ # Setup
+ await self.setup()
+
+ # Run tests
+ await self.test_simple_workflow()
+ await self.test_concurrent_workflows()
+ await self.test_task_queue_operations()
+ await self.test_event_bus_operations()
+ await self.test_agent_pool_operations()
+ await self.test_error_handling()
+
+ # Print summary
+ logger.info("\n" + "=" * 50)
+ logger.info("Test Summary:")
+ logger.info(f"Events published: {self.test_results['events_published']}")
+ logger.info(f"Tasks completed: {self.test_results['tasks_completed']}")
+ logger.info(f"Agents used: {self.test_results['agents_used']}")
+ logger.info(f"Errors encountered: {len(self.test_results['errors'])}")
+
+ # Cleanup
+ await self.cleanup()
+
+ logger.info("\nAll tests completed successfully! ✅")
+
+ except Exception as e:
+ logger.error(f"Test failed: {str(e)}")
+ self.test_results["errors"].append(str(e))
+ await self.cleanup()
+ raise
+
+
+async def main():
+ """Main test entry point"""
+ test = IntegrationTest()
+ await test.run_all_tests()
+
+
+if __name__ == "__main__":
+ # Run the integration test
+ asyncio.run(main())
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/.env b/backend/orchestrator_single.py/.env
new file mode 100644
index 000000000..26a0d00a0
--- /dev/null
+++ b/backend/orchestrator_single.py/.env
@@ -0,0 +1,2 @@
+GOOGLE_API_KEY=#add your api key
+PORT=8000
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/__pycache__/orchestrator_single.cpython-313.pyc b/backend/orchestrator_single.py/__pycache__/orchestrator_single.cpython-313.pyc
new file mode 100644
index 000000000..e7554e668
Binary files /dev/null and b/backend/orchestrator_single.py/__pycache__/orchestrator_single.cpython-313.pyc differ
diff --git a/backend/orchestrator_single.py/main.py b/backend/orchestrator_single.py/main.py
new file mode 100644
index 000000000..815eb10d0
--- /dev/null
+++ b/backend/orchestrator_single.py/main.py
@@ -0,0 +1,74 @@
+from flask import Flask, request, jsonify
+from flask_cors import CORS
+import base64
+from orchestrator_single import process_voice_sync
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+app = Flask(__name__)
+CORS(app)
+
+@app.route('/')
+def home():
+ return jsonify({
+ "message": "Life Witness Agent API",
+ "version": "1.0.0",
+ "endpoints": {
+ "/api/voice/process": "POST - Process voice input",
+ "/api/text/process": "POST - Process text input (for testing)",
+ "/api/health": "GET - Health check"
+ }
+ })
+
+@app.route('/api/voice/process', methods=['POST'])
+def process_voice():
+ """Process voice input"""
+ try:
+ data = request.json
+ audio_base64 = data.get('audio')
+
+ if not audio_base64:
+ return jsonify({"error": "No audio data provided"}), 400
+
+ # Decode base64 audio
+ audio_bytes = base64.b64decode(audio_base64)
+
+ # Process through orchestrator
+ result = process_voice_sync(audio_data=audio_bytes)
+
+ return jsonify(result)
+
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+@app.route('/api/text/process', methods=['POST'])
+def process_text():
+ """Process text input (for testing without audio)"""
+ try:
+ data = request.json
+ text = data.get('text', '')
+
+ if not text:
+ return jsonify({"error": "No text provided"}), 400
+
+ # Process through orchestrator
+ result = process_voice_sync(test_text=text)
+
+ return jsonify(result)
+
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+@app.route('/api/health', methods=['GET'])
+def health():
+ """Health check"""
+ return jsonify({
+ "status": "healthy",
+ "service": "life-witness-agent"
+ })
+
+if __name__ == '__main__':
+ port = int(os.getenv('PORT', 8000))
+ app.run(host='0.0.0.0', port=port, debug=True)
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/orchestrator_single.py b/backend/orchestrator_single.py/orchestrator_single.py
new file mode 100644
index 000000000..b8cd10c6c
--- /dev/null
+++ b/backend/orchestrator_single.py/orchestrator_single.py
@@ -0,0 +1,232 @@
+from typing import TypedDict, Dict, Any
+from langgraph.graph import Graph, END
+from langchain_google_genai import ChatGoogleGenerativeAI
+from langchain.memory import ConversationBufferMemory
+from langchain.schema import HumanMessage, AIMessage
+import google.generativeai as genai
+import base64
+import os
+from dotenv import load_dotenv
+import asyncio
+
+load_dotenv()
+
+# Configure Gemini
+genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
+
+# Initialize Gemini models
+gemini_model = genai.GenerativeModel('gemini-1.5-pro')
+gemini_flash = genai.GenerativeModel('gemini-1.5-flash')
+
+# Initialize LangChain Gemini for conversation
+llm = ChatGoogleGenerativeAI(
+ model="gemini-1.5-flash",
+ google_api_key=os.getenv("GOOGLE_API_KEY"),
+ temperature=0.7
+)
+
+# Initialize conversational memory
+memory = ConversationBufferMemory(
+ return_messages=True,
+ memory_key="chat_history"
+)
+
+# Simple memory storage
+stored_memories = []
+
+class VoiceAgent:
+ """Handles voice operations using Gemini"""
+ def __init__(self):
+ self.gemini_flash = gemini_flash
+
+ def transcribe(self, audio_data: bytes) -> str:
+ """For MVP, we'll skip actual audio transcription"""
+ # In production, you would upload audio and transcribe
+ # For now, return a placeholder
+ return "Transcribed audio content"
+
+ def synthesize(self, text: str) -> bytes:
+ """Mock audio synthesis"""
+ return b"mock_audio_data"
+
+class MemoryAgent:
+ """Manages conversation memory"""
+ def __init__(self, memory: ConversationBufferMemory):
+ self.memory = memory
+ self.gemini_model = gemini_model
+
+ def add_interaction(self, user_input: str, ai_response: str):
+ """Add user-AI interaction to memory"""
+ self.memory.chat_memory.add_user_message(user_input)
+ self.memory.chat_memory.add_ai_message(ai_response)
+
+ # Extract and store key information
+ if "remember" in user_input.lower() or "don't forget" in user_input.lower():
+ stored_memories.append({
+ "id": len(stored_memories) + 1,
+ "content": user_input,
+ "response": ai_response,
+ "type": "explicit_memory"
+ })
+
+ def get_context(self) -> str:
+ """Get conversation history as context"""
+ messages = self.memory.chat_memory.messages
+ context = []
+ for msg in messages[-10:]: # Last 10 messages
+ if isinstance(msg, HumanMessage):
+ context.append(f"User: {msg.content}")
+ elif isinstance(msg, AIMessage):
+ context.append(f"Assistant: {msg.content}")
+ return "\n".join(context)
+
+ def search_memories(self, query: str) -> list:
+ """Simple memory search"""
+ results = []
+ query_lower = query.lower()
+ for memory in stored_memories:
+ if query_lower in memory["content"].lower():
+ results.append(memory)
+ return results
+
+class ResponseAgent:
+ """Generate responses using Gemini"""
+ def __init__(self, memory_agent):
+ self.memory_agent = memory_agent
+ self.gemini_model = gemini_model
+
+ def generate(self, user_input: str) -> str:
+ """Generate contextual response"""
+ context = self.memory_agent.get_context()
+
+ # Check if user is asking about memories
+ is_memory_query = any(word in user_input.lower() for word in ["what", "when", "who", "remember", "told", "said"])
+
+ memories = []
+ if is_memory_query:
+ memories = self.memory_agent.search_memories(user_input)
+
+ # Build prompt
+ prompt = f"""You are a Life Witness Agent - a personal AI companion that helps people capture and remember their life experiences.
+
+Previous conversation:
+{context}
+
+{"Relevant memories found: " + str(memories) if memories else ""}
+
+User's current message: {user_input}
+
+Instructions:
+- If the user wants to remember something, acknowledge it warmly and confirm you'll remember it
+- If they're asking about past memories, help them recall based on conversation history
+- Be conversational, warm, and show genuine interest
+- Keep responses concise but meaningful
+
+Response:"""
+
+ # Generate response using Gemini
+ response = self.gemini_model.generate_content(prompt)
+ return response.text.strip()
+
+# Initialize agents
+voice_agent = VoiceAgent()
+memory_agent = MemoryAgent(memory)
+response_agent = ResponseAgent(memory_agent)
+
+# Define state schema
+class AgentState(TypedDict):
+ audio_input: bytes
+ transcribed_text: str
+ response_text: str
+ audio_output: bytes
+ error: str
+
+# Node functions
+def transcribe_audio(state: AgentState) -> AgentState:
+ """Convert audio to text"""
+ try:
+ if state.get("audio_input"):
+ text = voice_agent.transcribe(state["audio_input"])
+ state["transcribed_text"] = text
+ else:
+ # For testing without audio
+ state["transcribed_text"] = state.get("test_text", "Hello")
+ except Exception as e:
+ state["error"] = f"Transcription error: {str(e)}"
+ state["transcribed_text"] = "Could not understand audio"
+ return state
+
+def generate_response(state: AgentState) -> AgentState:
+ """Generate AI response with memory context"""
+ try:
+ user_input = state["transcribed_text"]
+ response = response_agent.generate(user_input)
+ state["response_text"] = response
+
+ # Update memory with this interaction
+ memory_agent.add_interaction(user_input, response)
+
+ except Exception as e:
+ state["error"] = f"Response generation error: {str(e)}"
+ state["response_text"] = "I'm sorry, I couldn't process that request."
+ return state
+
+def synthesize_speech(state: AgentState) -> AgentState:
+ """Convert response text to speech"""
+ try:
+ audio_data = voice_agent.synthesize(state["response_text"])
+ state["audio_output"] = audio_data
+ except Exception as e:
+ state["error"] = f"Speech synthesis error: {str(e)}"
+ state["audio_output"] = b""
+ return state
+
+# Build the LangGraph workflow
+workflow = Graph()
+
+# Add nodes
+workflow.add_node("transcribe", transcribe_audio)
+workflow.add_node("generate", generate_response)
+workflow.add_node("synthesize", synthesize_speech)
+
+# Define edges
+workflow.add_edge("transcribe", "generate")
+workflow.add_edge("generate", "synthesize")
+workflow.add_edge("synthesize", END)
+
+# Set entry point
+workflow.set_entry_point("transcribe")
+
+# Compile the graph
+app = workflow.compile()
+
+# Main orchestration function
+async def process_voice_input(audio_data: bytes = None, test_text: str = None) -> Dict[str, Any]:
+ """Process voice input through the workflow"""
+ initial_state = {
+ "audio_input": audio_data,
+ "transcribed_text": "",
+ "response_text": "",
+ "audio_output": b"",
+ "error": "",
+ "test_text": test_text
+ }
+
+ # Run the workflow
+ result = await app.ainvoke(initial_state)
+
+ return {
+ "transcribed_text": result.get("transcribed_text"),
+ "response_text": result.get("response_text"),
+ "audio_output": base64.b64encode(result.get("audio_output", b"")).decode('utf-8'),
+ "error": result.get("error"),
+ "conversation_length": len(memory.chat_memory.messages),
+ "total_memories": len(stored_memories)
+ }
+
+# Synchronous wrapper for Flask
+def process_voice_sync(audio_data: bytes = None, test_text: str = None) -> Dict[str, Any]:
+ """Synchronous wrapper for the async orchestration"""
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+ return loop.run_until_complete(process_voice_input(audio_data, test_text))
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/requirements.txt b/backend/orchestrator_single.py/requirements.txt
new file mode 100644
index 000000000..03cdb635a
--- /dev/null
+++ b/backend/orchestrator_single.py/requirements.txt
@@ -0,0 +1,7 @@
+flask==3.0.0
+flask-cors==4.0.0
+python-dotenv==1.0.0
+langchain==0.1.0
+langchain-google-genai==0.0.5
+langgraph==0.0.20
+google-generativeai>=0.3.2
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/test_mvp.py b/backend/orchestrator_single.py/test_mvp.py
new file mode 100644
index 000000000..2028e5cf0
--- /dev/null
+++ b/backend/orchestrator_single.py/test_mvp.py
@@ -0,0 +1,72 @@
+import requests
+import time
+import json
+
+base_url = "http://localhost:8000"
+
+print("=== Life Witness Agent - Enhanced Gemini Test ===\n")
+
+# Test 1: Store a memory with automatic function calling
+print("Test 1: Storing a detailed memory")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "I just had an amazing lunch with my colleague Sarah at Chez Pierre restaurant. We discussed the new AI project and she gave me great advice about leadership. I'm feeling really inspired!"}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"Function calls: {json.dumps(result.get('function_calls', []), indent=2)}\n")
+time.sleep(2)
+
+# Test 2: Store another memory
+print("Test 2: Family memory")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "Remember that my nephew Jake's birthday is next week. He's turning 9 and loves robotics. I should get him that Arduino kit we saw."}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"Function calls: {json.dumps(result.get('function_calls', []), indent=2)}\n")
+time.sleep(2)
+
+# Test 3: Query memories
+print("Test 3: Searching for memories")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "What did Sarah tell me about?"}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"Function calls: {json.dumps(result.get('function_calls', []), indent=2)}\n")
+time.sleep(2)
+
+# Test 4: Complex query
+print("Test 4: Complex memory query")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "What upcoming events do I need to remember?"}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"Function calls: {json.dumps(result.get('function_calls', []), indent=2)}\n")
+time.sleep(2)
+
+# Test 5: Emotional memory
+print("Test 5: Emotional context")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "I'm feeling a bit overwhelmed with work. My manager just assigned me three new projects and I don't know how I'll manage everything."}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"Function calls: {json.dumps(result.get('function_calls', []), indent=2)}\n")
+
+# Test 6: Memory statistics
+print("\nTest 6: Conversation summary")
+response = requests.post(
+ f"{base_url}/api/text/process",
+ json={"text": "Can you summarize what we've talked about today?"}
+)
+result = response.json()
+print(f"Response: {result['response_text']}")
+print(f"\nTotal memories stored: {result.get('total_memories', 0)}")
+print(f"Conversation length: {result.get('conversation_length', 0)} messages")
\ No newline at end of file
diff --git a/backend/orchestrator_single.py/voice_routes_single.py b/backend/orchestrator_single.py/voice_routes_single.py
new file mode 100644
index 000000000..815eb10d0
--- /dev/null
+++ b/backend/orchestrator_single.py/voice_routes_single.py
@@ -0,0 +1,74 @@
+from flask import Flask, request, jsonify
+from flask_cors import CORS
+import base64
+from orchestrator_single import process_voice_sync
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+app = Flask(__name__)
+CORS(app)
+
+@app.route('/')
+def home():
+ return jsonify({
+ "message": "Life Witness Agent API",
+ "version": "1.0.0",
+ "endpoints": {
+ "/api/voice/process": "POST - Process voice input",
+ "/api/text/process": "POST - Process text input (for testing)",
+ "/api/health": "GET - Health check"
+ }
+ })
+
+@app.route('/api/voice/process', methods=['POST'])
+def process_voice():
+ """Process voice input"""
+ try:
+ data = request.json
+ audio_base64 = data.get('audio')
+
+ if not audio_base64:
+ return jsonify({"error": "No audio data provided"}), 400
+
+ # Decode base64 audio
+ audio_bytes = base64.b64decode(audio_base64)
+
+ # Process through orchestrator
+ result = process_voice_sync(audio_data=audio_bytes)
+
+ return jsonify(result)
+
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+@app.route('/api/text/process', methods=['POST'])
+def process_text():
+ """Process text input (for testing without audio)"""
+ try:
+ data = request.json
+ text = data.get('text', '')
+
+ if not text:
+ return jsonify({"error": "No text provided"}), 400
+
+ # Process through orchestrator
+ result = process_voice_sync(test_text=text)
+
+ return jsonify(result)
+
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+@app.route('/api/health', methods=['GET'])
+def health():
+ """Health check"""
+ return jsonify({
+ "status": "healthy",
+ "service": "life-witness-agent"
+ })
+
+if __name__ == '__main__':
+ port = int(os.getenv('PORT', 8000))
+ app.run(host='0.0.0.0', port=port, debug=True)
\ No newline at end of file
diff --git a/backend/requirements.txt b/backend/requirements.txt
new file mode 100644
index 000000000..09dd34f7e
--- /dev/null
+++ b/backend/requirements.txt
@@ -0,0 +1,38 @@
+fastapi
+pydantic
+uvicorn[standard]
+google-cloud-firestore==2.13.1
+google-cloud-storage>=2.14.0,<3.0.0
+google-cloud-tasks
+google-cloud-secret-manager
+google-api-python-client
+google-auth
+# Add other Google Cloud packages as needed
+
+langchain
+langchain-google-genai
+langgraph
+python-dotenv
+dataclasses-json
+fastapi
+pydantic
+# Standard library modules (no need to install): asyncio, logging, typing, datetime, enum, uuid, json, time, weakref, collections, importlib
+
+# Core dependencies for LangChain and Google Vertex AI LLM integration
+langchain==0.0.202
+langchain-google-vertexai==0.0.5
+
+# Google Cloud AI SDKs
+google-cloud-aiplatform
+google-cloud-speech==2.15.1
+google-cloud-texttospeech==2.15.1
+
+# Audio processing
+pydub==0.25.1
+
+# Optional but recommended for environment management
+python-dotenv==1.0.0
+
+# For testing if needed
+pytest==7.4.0
+pytest-asyncio==0.21.0
diff --git a/backend/services/__pycache__/gemini_service.cpython-313.pyc b/backend/services/__pycache__/gemini_service.cpython-313.pyc
new file mode 100644
index 000000000..8485fed12
Binary files /dev/null and b/backend/services/__pycache__/gemini_service.cpython-313.pyc differ
diff --git a/backend/services/calendar_service.py b/backend/services/calendar_service.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/services/gemini_service.py b/backend/services/gemini_service.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/services/gmail_service.py b/backend/services/gmail_service.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/services/storage_service.py b/backend/services/storage_service.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/utils/config.py b/backend/utils/config.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/utils/error_handling.py b/backend/utils/error_handling.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/utils/logging_config.py b/backend/utils/logging_config.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/utils/metrics.py b/backend/utils/metrics.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/utils/tracing.py b/backend/utils/tracing.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/cache/api_cache.json b/data/cache/api_cache.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/demo/sample_memories.json b/data/demo/sample_memories.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/memories/embeddings.json b/data/memories/embeddings.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/memories/events.json b/data/memories/events.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/memories/relationships.json b/data/memories/relationships.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 000000000..e366ef730
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,18 @@
+version: '3.8'
+
+services:
+ backend:
+ build: ./backend
+ ports:
+ - "8080:8080"
+ environment:
+ - PYTHONUNBUFFERED=1
+
+ frontend:
+ build: ./frontend
+ ports:
+ - "3000:3000"
+ environment:
+ - CHOKIDAR_USEPOLLING=true
+ depends_on:
+ - backend
\ No newline at end of file
diff --git a/frontend/.env.local.example b/frontend/.env.local.example
new file mode 100755
index 000000000..cb921520b
--- /dev/null
+++ b/frontend/.env.local.example
@@ -0,0 +1 @@
+NEXT_PUBLIC_AGENT_API_URL="backend URL here"
\ No newline at end of file
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 000000000..24c06b8c8
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
new file mode 100644
index 000000000..0c25989ac
--- /dev/null
+++ b/frontend/Dockerfile
@@ -0,0 +1,13 @@
+FROM node:20-alpine
+
+WORKDIR /app
+
+COPY package.json ./
+COPY package-lock.json ./
+RUN npm install
+
+COPY . .
+
+RUN npm run build
+
+CMD ["npm", "start"]
\ No newline at end of file
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 000000000..e215bc4cc
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
new file mode 100644
index 000000000..c85fb67c4
--- /dev/null
+++ b/frontend/eslint.config.mjs
@@ -0,0 +1,16 @@
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+import { FlatCompat } from "@eslint/eslintrc";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const compat = new FlatCompat({
+ baseDirectory: __dirname,
+});
+
+const eslintConfig = [
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
+];
+
+export default eslintConfig;
diff --git a/frontend/global.d.ts b/frontend/global.d.ts
new file mode 100644
index 000000000..1b01b2ebf
--- /dev/null
+++ b/frontend/global.d.ts
@@ -0,0 +1,57 @@
+// Add this to your global.d.ts file or at the top of your VoiceInterface component file
+
+interface SpeechRecognition extends EventTarget {
+ continuous: boolean;
+ interimResults: boolean;
+ lang: string;
+ start(): void;
+ stop(): void;
+ onstart: ((this: SpeechRecognition, ev: Event) => any) | null;
+ onend: ((this: SpeechRecognition, ev: Event) => any) | null;
+ onresult: ((this: SpeechRecognition, ev: SpeechRecognitionEvent) => any) | null;
+ onerror: ((this: SpeechRecognition, ev: SpeechRecognitionErrorEvent) => any) | null;
+}
+
+interface SpeechRecognitionEvent extends Event {
+ resultIndex: number;
+ results: SpeechRecognitionResultList;
+}
+
+interface SpeechRecognitionResultList {
+ length: number;
+ [index: number]: SpeechRecognitionResult;
+}
+
+interface SpeechRecognitionResult {
+ length: number;
+ isFinal: boolean;
+ [index: number]: SpeechRecognitionAlternative;
+}
+
+interface SpeechRecognitionAlternative {
+ transcript: string;
+ confidence: number;
+}
+
+interface SpeechRecognitionErrorEvent extends Event {
+ error: string;
+ message: string;
+}
+
+declare var SpeechRecognition: {
+ prototype: SpeechRecognition;
+ new(): SpeechRecognition;
+};
+
+declare var webkitSpeechRecognition: {
+ prototype: SpeechRecognition;
+ new(): SpeechRecognition;
+};
+
+// Extend the Window interface
+declare global {
+ interface Window {
+ SpeechRecognition: typeof SpeechRecognition;
+ webkitSpeechRecognition: typeof SpeechRecognition;
+ }
+}
\ No newline at end of file
diff --git a/frontend/next.config.ts b/frontend/next.config.ts
new file mode 100644
index 000000000..e9ffa3083
--- /dev/null
+++ b/frontend/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+};
+
+export default nextConfig;
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
new file mode 100644
index 000000000..985c09fc9
--- /dev/null
+++ b/frontend/package-lock.json
@@ -0,0 +1,6212 @@
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.1.0",
+ "dependencies": {
+ "framer-motion": "^12.23.9",
+ "next": "15.4.4",
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ "swiper": "^11.2.10"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.4.4",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz",
+ "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
+ "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+ "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
+ "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
+ "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
+ "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
+ "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
+ "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
+ "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
+ "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
+ "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
+ "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
+ "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
+ "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
+ "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
+ "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
+ "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
+ "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
+ "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
+ "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
+ "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
+ "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
+ "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
+ "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
+ "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.4.4"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
+ "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
+ "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
+ "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
+ "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.29",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
+ "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.4.tgz",
+ "integrity": "sha512-SJKOOkULKENyHSYXE5+KiFU6itcIb6wSBjgM92meK0HVKpo94dNOLZVdLLuS7/BxImROkGoPsjR4EnuDucqiiA==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.4.tgz",
+ "integrity": "sha512-1FDsyN//ai3Jd97SEd7scw5h1yLdzDACGOPRofr2GD3sEFsBylEEoL0MHSerd4n2dq9Zm/mFMqi4+NRMOreOKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.4.tgz",
+ "integrity": "sha512-eVG55dnGwfUuG+TtnUCt+mEJ+8TGgul6nHEvdb8HEH7dmJIFYOCApAaFrIrxwtEq2Cdf+0m5sG1Np8cNpw9EAw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.4.tgz",
+ "integrity": "sha512-zqG+/8apsu49CltEj4NAmCGZvHcZbOOOsNoTVeIXphYWIbE4l6A/vuQHyqll0flU2o3dmYCXsBW5FmbrGDgljQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.4.tgz",
+ "integrity": "sha512-LRD4l2lq4R+2QCHBQVC0wjxxkLlALGJCwigaJ5FSRSqnje+MRKHljQNZgDCaKUZQzO/TXxlmUdkZP/X3KNGZaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.4.tgz",
+ "integrity": "sha512-LsGUCTvuZ0690fFWerA4lnQvjkYg9gHo12A3wiPUR4kCxbx/d+SlwmonuTH2SWZI+RVGA9VL3N0S03WTYv6bYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.4.tgz",
+ "integrity": "sha512-aOy5yNRpLL3wNiJVkFYl6w22hdREERNjvegE6vvtix8LHRdsTHhWTpgvcYdCK7AIDCQW5ATmzr9XkPHvSoAnvg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.4.tgz",
+ "integrity": "sha512-FL7OAn4UkR8hKQRGBmlHiHinzOb07tsfARdGh7v0Z0jEJ3sz8/7L5bR23ble9E6DZMabSStqlATHlSxv1fuzAg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.4.tgz",
+ "integrity": "sha512-eEdNW/TXwjYhOulQh0pffTMMItWVwKCQpbziSBmgBNFZIIRn2GTXrhrewevs8wP8KXWYMx8Z+mNU0X+AfvtrRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.4.tgz",
+ "integrity": "sha512-SE5pYNbn/xZKMy1RE3pAs+4xD32OI4rY6mzJa4XUkp/ItZY+OMjIgilskmErt8ls/fVJ+Ihopi2QIeW6O3TrMw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rushstack/eslint-patch": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz",
+ "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz",
+ "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz",
+ "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-x64": "4.1.11",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.11",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.11",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.11",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz",
+ "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz",
+ "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz",
+ "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz",
+ "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz",
+ "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz",
+ "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz",
+ "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz",
+ "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz",
+ "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz",
+ "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@emnapi/wasi-threads": "^1.0.2",
+ "@napi-rs/wasm-runtime": "^0.2.11",
+ "@tybys/wasm-util": "^0.9.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz",
+ "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz",
+ "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz",
+ "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.11",
+ "@tailwindcss/oxide": "4.1.11",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.11"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
+ "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz",
+ "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.8",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
+ "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.6",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
+ "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
+ "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.38.0",
+ "@typescript-eslint/type-utils": "8.38.0",
+ "@typescript-eslint/utils": "8.38.0",
+ "@typescript-eslint/visitor-keys": "8.38.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.38.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
+ "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.38.0",
+ "@typescript-eslint/types": "8.38.0",
+ "@typescript-eslint/typescript-estree": "8.38.0",
+ "@typescript-eslint/visitor-keys": "8.38.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
+ "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.38.0",
+ "@typescript-eslint/types": "^8.38.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
+ "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.38.0",
+ "@typescript-eslint/visitor-keys": "8.38.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
+ "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
+ "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.38.0",
+ "@typescript-eslint/typescript-estree": "8.38.0",
+ "@typescript-eslint/utils": "8.38.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
+ "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
+ "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.38.0",
+ "@typescript-eslint/tsconfig-utils": "8.38.0",
+ "@typescript-eslint/types": "8.38.0",
+ "@typescript-eslint/visitor-keys": "8.38.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
+ "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.38.0",
+ "@typescript-eslint/types": "8.38.0",
+ "@typescript-eslint/typescript-estree": "8.38.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.38.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
+ "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.38.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
+ "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz",
+ "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz",
+ "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz",
+ "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz",
+ "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz",
+ "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz",
+ "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz",
+ "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz",
+ "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz",
+ "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz",
+ "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz",
+ "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz",
+ "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz",
+ "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz",
+ "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz",
+ "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz",
+ "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz",
+ "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+ "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.10.3",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
+ "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001727",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
+ "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
+ "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
+ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
+ "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.0",
+ "@eslint/core": "^0.15.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.32.0",
+ "@eslint/plugin-kit": "^0.3.4",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.4.tgz",
+ "integrity": "sha512-sK/lWLUVF5om18O5w76Jt3F8uzu/LP5mVa6TprCMWkjWHUmByq80iHGHcdH7k1dLiJlj+DRIWf98d5piwRsSuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@next/eslint-plugin-next": "15.4.4",
+ "@rushstack/eslint-patch": "^1.10.3",
+ "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-react": "^7.37.0",
+ "eslint-plugin-react-hooks": "^5.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "12.23.9",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.9.tgz",
+ "integrity": "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.9",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
+ "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.23.9",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.9.tgz",
+ "integrity": "sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-postinstall": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz",
+ "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/next": {
+ "version": "15.4.4",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.4.4.tgz",
+ "integrity": "sha512-kNcubvJjOL9yUOfwtZF3HfDhuhp+kVD+FM2A6Tyua1eI/xfmY4r/8ZS913MMz+oWKDlbps/dQOWdDricuIkXLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.4.4",
+ "@swc/helpers": "0.5.15",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.4.4",
+ "@next/swc-darwin-x64": "15.4.4",
+ "@next/swc-linux-arm64-gnu": "15.4.4",
+ "@next/swc-linux-arm64-musl": "15.4.4",
+ "@next/swc-linux-x64-gnu": "15.4.4",
+ "@next/swc-linux-x64-musl": "15.4.4",
+ "@next/swc-win32-arm64-msvc": "15.4.4",
+ "@next/swc-win32-x64-msvc": "15.4.4",
+ "sharp": "^0.34.3"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "devOptional": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
+ "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.4",
+ "semver": "^7.7.2"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.3",
+ "@img/sharp-darwin-x64": "0.34.3",
+ "@img/sharp-libvips-darwin-arm64": "1.2.0",
+ "@img/sharp-libvips-darwin-x64": "1.2.0",
+ "@img/sharp-libvips-linux-arm": "1.2.0",
+ "@img/sharp-libvips-linux-arm64": "1.2.0",
+ "@img/sharp-libvips-linux-ppc64": "1.2.0",
+ "@img/sharp-libvips-linux-s390x": "1.2.0",
+ "@img/sharp-libvips-linux-x64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0",
+ "@img/sharp-linux-arm": "0.34.3",
+ "@img/sharp-linux-arm64": "0.34.3",
+ "@img/sharp-linux-ppc64": "0.34.3",
+ "@img/sharp-linux-s390x": "0.34.3",
+ "@img/sharp-linux-x64": "0.34.3",
+ "@img/sharp-linuxmusl-arm64": "0.34.3",
+ "@img/sharp-linuxmusl-x64": "0.34.3",
+ "@img/sharp-wasm32": "0.34.3",
+ "@img/sharp-win32-arm64": "0.34.3",
+ "@img/sharp-win32-ia32": "0.34.3",
+ "@img/sharp-win32-x64": "0.34.3"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/swiper": {
+ "version": "11.2.10",
+ "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.10.tgz",
+ "integrity": "sha512-RMeVUUjTQH+6N3ckimK93oxz6Sn5la4aDlgPzB+rBrG/smPdCTicXyhxa+woIpopz+jewEloiEE3lKo1h9w2YQ==",
+ "funding": [
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/swiperjs"
+ },
+ {
+ "type": "open_collective",
+ "url": "http://opencollective.com/swiper"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.7.0"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
+ "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
+ "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+ "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+ "@unrs/resolver-binding-android-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+ "@unrs/resolver-binding-darwin-x64": "1.11.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 000000000..d0ece1f37
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build --no-lint",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "framer-motion": "^12.23.9",
+ "next": "15.4.4",
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ "swiper": "^11.2.10"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.4.4",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs
new file mode 100644
index 000000000..c7bcb4b1e
--- /dev/null
+++ b/frontend/postcss.config.mjs
@@ -0,0 +1,5 @@
+const config = {
+ plugins: ["@tailwindcss/postcss"],
+};
+
+export default config;
diff --git a/frontend/public/Coffee Meetup.jpeg b/frontend/public/Coffee Meetup.jpeg
new file mode 100644
index 000000000..2bf12c5e3
Binary files /dev/null and b/frontend/public/Coffee Meetup.jpeg differ
diff --git a/frontend/public/Day at park.jpeg b/frontend/public/Day at park.jpeg
new file mode 100644
index 000000000..ce859af60
Binary files /dev/null and b/frontend/public/Day at park.jpeg differ
diff --git a/frontend/public/Jakes Birthday.jpeg b/frontend/public/Jakes Birthday.jpeg
new file mode 100644
index 000000000..d0b1e5f09
Binary files /dev/null and b/frontend/public/Jakes Birthday.jpeg differ
diff --git a/frontend/public/Jennifer.jpeg b/frontend/public/Jennifer.jpeg
new file mode 100644
index 000000000..323e7cb7f
Binary files /dev/null and b/frontend/public/Jennifer.jpeg differ
diff --git a/frontend/public/Team Lunch.jpeg b/frontend/public/Team Lunch.jpeg
new file mode 100644
index 000000000..4605aae91
Binary files /dev/null and b/frontend/public/Team Lunch.jpeg differ
diff --git a/frontend/public/file.svg b/frontend/public/file.svg
new file mode 100644
index 000000000..004145cdd
--- /dev/null
+++ b/frontend/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/globe.svg b/frontend/public/globe.svg
new file mode 100644
index 000000000..567f17b0d
--- /dev/null
+++ b/frontend/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/next.svg b/frontend/public/next.svg
new file mode 100644
index 000000000..5174b28c5
--- /dev/null
+++ b/frontend/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/placeholder.png b/frontend/public/placeholder.png
new file mode 100644
index 000000000..df97f5f31
Binary files /dev/null and b/frontend/public/placeholder.png differ
diff --git a/frontend/public/vercel.svg b/frontend/public/vercel.svg
new file mode 100644
index 000000000..770539603
--- /dev/null
+++ b/frontend/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/window.svg b/frontend/public/window.svg
new file mode 100644
index 000000000..b2b2a44f6
--- /dev/null
+++ b/frontend/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico
new file mode 100644
index 000000000..718d6fea4
Binary files /dev/null and b/frontend/src/app/favicon.ico differ
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
new file mode 100644
index 000000000..a2dc41ece
--- /dev/null
+++ b/frontend/src/app/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
new file mode 100644
index 000000000..f7fa87eb8
--- /dev/null
+++ b/frontend/src/app/layout.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
new file mode 100644
index 000000000..dd52629c0
--- /dev/null
+++ b/frontend/src/app/page.tsx
@@ -0,0 +1,519 @@
+"use client";
+
+// Importing the tools I need from React and the Swiper carousel library.
+import 'swiper/css';
+import 'swiper/css/effect-coverflow';
+import 'swiper/css/pagination';
+import { callAgentAPI } from "@/services/apiClient";
+import Image from 'next/image';
+import React, { useState, useRef, useEffect } from "react";
+import { Swiper, SwiperSlide } from "swiper/react";
+import { EffectCoverflow, Pagination, Autoplay } from "swiper/modules";
+import type { Swiper as SwiperCore } from "swiper";
+import { motion, AnimatePresence } from "framer-motion";
+import "swiper/css";
+import "swiper/css/effect-coverflow";
+import "swiper/css/pagination";
+
+import PhotoUploadModal from "../components/photoUpload";
+import VoiceInterface from "../components/voiceInterface";
+import { voiceService, type VoiceQueryResult } from "../services/voiceService";
+
+const TypewriterText = ({
+ text,
+ onComplete,
+ className,
+}: {
+ text: string;
+ onComplete?: () => void;
+ className?: string;
+}) => {
+ const [displayedText, setDisplayedText] = useState("");
+
+ useEffect(() => {
+ let currentText = "";
+ setDisplayedText("");
+ const words = text.split(" ");
+ let currentWordIndex = 0;
+ const intervalId = setInterval(() => {
+ if (currentWordIndex < words.length) {
+ currentText =
+ currentText +
+ (currentWordIndex > 0 ? " " : "") +
+ words[currentWordIndex];
+ setDisplayedText(currentText);
+ currentWordIndex++;
+ } else {
+ clearInterval(intervalId);
+ if (onComplete) onComplete();
+ }
+ }, 120);
+ return () => clearInterval(intervalId);
+ }, [text, onComplete]);
+ return {displayedText}
;
+};
+
+export default function Home() {
+ const [view, setView] = useState<"home" | "thinking" | "chat">("home");
+ const [conversation, setConversation] = useState<
+ { sender: "user" | "agent"; query: string; response?: string }[]
+ >([]);
+ const [currentStepIndex, setCurrentStepIndex] = useState(0);
+ const [currentUserQuery, setCurrentUserQuery] = useState('');
+ const [currentAgentReply, setCurrentAgentReply] = useState('');
+ const [highlightedMemoryId, setHighlightedMemoryId] = useState(null);
+ const [agentReply, setAgentReply] = useState("");
+
+ // This ref gives me direct control over the carousel component.
+ const [isInitiated, setIsInitiated] = useState(false);
+ const carouselRef = useRef(null);
+ const [activeIndex, setActiveIndex] = useState(0);
+ const chatContainerRef = useRef(null);
+
+ const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false);
+ const [showPhotoUploadLeft, setShowPhotoUploadLeft] = useState(false);
+
+
+ const [isVoiceListening, setIsVoiceListening] = useState(false);
+ const [isProcessingVoice, setIsProcessingVoice] = useState(false);
+ const [voiceTranscript, setVoiceTranscript] = useState("");
+
+ const [actualUserQueries, setActualUserQueries] = useState([]);
+ const [isUsingRealQueries, setIsUsingRealQueries] = useState(false);
+
+ const memoryPhotos = [
+ { id: 1, name: "Jennifer Chen", imageUrl: "/Jennifer.jpeg" },
+ { id: 2, name: "Jake's Bday", imageUrl: "/Jakes Birthday.jpeg" },
+ { id: 3, name: "Coffee Meetup", imageUrl: "/Coffee Meetup.jpeg" },
+ { id: 4, name: "Team Lunch", imageUrl: "/Team Lunch.jpeg" },
+ { id: 5, name: "Day at Park", imageUrl: "/Day at park.jpeg" },
+ ];
+
+ useEffect(() => {
+ if (chatContainerRef.current) {
+ const { scrollHeight, clientHeight } = chatContainerRef.current;
+ chatContainerRef.current.scrollTo({
+ top: scrollHeight - clientHeight,
+ behavior: "smooth",
+ });
+ }
+ }, [conversation]);
+
+ const wait = (ms: number) => new Promise((res) => setTimeout(res, ms));
+
+ const switchToThinking = () => {
+ setView("thinking");
+ };
+
+ const handleVoiceTranscript = async (transcript: string, isFinal: boolean) => {
+ setCurrentUserQuery(transcript);
+ setVoiceTranscript(transcript);
+ };
+
+ const handleVoiceStart = () => {
+ setCurrentUserQuery("");
+ setVoiceTranscript("");
+ };
+
+ const handleVoiceEnd = () => {};
+
+ const handleVoiceError = (error: string) => {
+ console.error("Voice error:", error);
+ setIsVoiceListening(false);
+ setIsProcessingVoice(false);
+ };
+
+ const toggleVoiceListening = async () => {
+ if (isProcessingVoice) return;
+
+ if (isVoiceListening) {
+ setIsVoiceListening(false);
+
+ if (voiceTranscript.trim()) {
+ setIsProcessingVoice(true);
+
+ try {
+ const result: VoiceQueryResult = await voiceService.processVoiceQuery(
+ voiceTranscript
+ );
+
+ let adjustedResponse = result.response;
+
+ // SPECIAL OVERRIDE for Jake's Bday
+ if (
+ voiceTranscript
+ .toLowerCase()
+ .includes("jake") &&
+ voiceTranscript.toLowerCase().includes("bday")
+ ) {
+ adjustedResponse = "I'm here! What's happening right now?";
+ }
+
+ if (result.success && adjustedResponse) {
+ if (
+ voiceTranscript.toLowerCase().includes("i want to remember this moment")
+ ) {
+ setShowPhotoUploadLeft(true);
+ }
+ await handleVoiceQuery(voiceTranscript, adjustedResponse);
+ } else {
+ setCurrentUserQuery(
+ "I'm sorry, I couldn't process your request. Please try again."
+ );
+ }
+ } catch (error) {
+ console.error("Error processing voice query:", error);
+ setCurrentUserQuery(
+ "I'm experiencing some technical difficulties. Please try again."
+ );
+ } finally {
+ setIsProcessingVoice(false);
+ }
+ }
+ } else {
+ setIsVoiceListening(true);
+ }
+ };
+
+ const handleVoiceQuery = async (query: string, response: string) => {
+ if (view === "home") {
+ setIsInitiated(true);
+ setIsUsingRealQueries(true);
+ setActualUserQueries([query]);
+ if (carouselRef.current) carouselRef.current.autoplay.stop();
+
+ setView("thinking");
+ await wait(2000);
+
+ setConversation([{ sender: "user", query: query }]);
+ setView("chat");
+
+ const words = query.split(" ");
+ const typewriterDuration = words.length * 120 + 500;
+ await wait(typewriterDuration);
+
+ setConversation((prev) => [
+ ...prev,
+ { sender: "agent", query: "", response: response },
+ ]);
+ setCurrentStepIndex(1);
+ } else if (view === "chat") {
+ const newQueryIndex = actualUserQueries.length;
+ setActualUserQueries((prev) => [...prev, query]);
+ setConversation((prev) => [...prev, { sender: "user", query: query }]);
+
+ const words = query.split(" ");
+ const typewriterDuration = words.length * 120 + 500;
+ await wait(typewriterDuration);
+
+ setConversation((prev) => [
+ ...prev,
+ { sender: "agent", query: "", response: response },
+ ]);
+ setCurrentStepIndex((prev) => prev + 1);
+ }
+ };
+
+ const handleGoHome = () => {
+ setView("home");
+ setConversation([]);
+ setCurrentStepIndex(0);
+ setIsInitiated(false);
+ setIsUsingRealQueries(false);
+ setActualUserQueries([]);
+ setCurrentUserQuery("");
+ setVoiceTranscript("");
+ setIsVoiceListening(false);
+ setIsProcessingVoice(false);
+ setShowPhotoUploadLeft(false);
+ voiceService.resetConversation();
+ if (carouselRef.current) {
+ carouselRef.current.autoplay?.start();
+ }
+ };
+
+ const handleAskAgent = async (query: string) => {
+ const result = await callAgentAPI(query);
+ setAgentReply(result.response || JSON.stringify(result));
+ };
+
+ return (
+
+ {/* This div blurs the background when the modal is open */}
+
+
+ {view === "home" && (
+
+
+
+
+
+
+
+ Hello,
+ {" "}
+ what memory
+
+
+ would you like to inquire about today?
+
+
+ {
+ carouselRef.current = swiper;
+ }}
+ onSlideChange={(swiper) => setActiveIndex(swiper.realIndex)}
+ effect={"coverflow"}
+ grabCursor={true}
+ centeredSlides={true}
+ slidesPerView={3}
+ loop={true}
+ autoplay={{ delay: 3000, disableOnInteraction: false }}
+ modules={[EffectCoverflow, Pagination, Autoplay]}
+ className="w-full"
+ coverflowEffect={{
+ rotate: 0,
+ stretch: 80,
+ depth: 150,
+ modifier: 1,
+ slideShadows: false,
+ }}
+ >
+ {memoryPhotos.map((photo, index) => (
+
+
+

+
+ {photo.name}
+
+
+
+ ))}
+
+
+
+ {isInitiated ? (
+
+ ) : (
+
+ {isProcessingVoice
+ ? "Processing your request..."
+ : isVoiceListening && voiceTranscript
+ ? voiceTranscript
+ : currentUserQuery || "Ask or talk about a memory..."}
+
+ )}
+
+ {/* UPDATED: Use controlled VoiceInterface */}
+
+ {/* --- PARTNER'S ADDITION: onClick handler for the upload button --- */}
+
+
+
+
+
+ )}
+
+
+
+ {view === "thinking" && (
+
+ "{actualUserQueries[0] || ""}"
+
+
+
+
+
+
+ )}
+
+
+
+ {view === "chat" && (
+
+
+ {/* Left Panel */}
+
+
+
+ {showPhotoUploadLeft ? (
+
+
{}}
+ onPhotoUpload={(photo) =>
+ console.log("Photo uploaded during memory creation:", photo)
+ }
+ />
+
+ ) : (
+
+

+
Jennifer Chen
+
+ )}
+
+
+
+ {/* Right Panel */}
+
+
+ {conversation.map((chat, index) => (
+
+ {chat.sender === "user" ? (
+
+ ) : (
+
+ )}
+
+ ))}
+
+
+ {/* Chat input area */}
+
+ {/* Voice interface */}
+
+
+
+
+
+
+
+ )}
+
+
+
+ {/* SHIVANI ADDITION: The modal component itself */}
+
setIsPhotoModalOpen(false)}
+ onPhotoUpload={(photo) => {
+ // This is where you'd handle the uploaded photo data
+ console.log("New photo uploaded:", photo);
+ setIsPhotoModalOpen(false); // Close modal after upload
+ }}
+ />
+
+ );
+}
diff --git a/frontend/src/components/agentStatus.tsx b/frontend/src/components/agentStatus.tsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/components/memoryCard.tsx b/frontend/src/components/memoryCard.tsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/components/memoryTimeline.tsx b/frontend/src/components/memoryTimeline.tsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/components/photoUpload.tsx b/frontend/src/components/photoUpload.tsx
new file mode 100644
index 000000000..95e23915e
--- /dev/null
+++ b/frontend/src/components/photoUpload.tsx
@@ -0,0 +1,255 @@
+import React, { useState, useRef } from "react";
+
+// Define the types for the component
+interface PhotoData {
+ id: number;
+ name: string;
+ imageUrl: string;
+ file: File;
+}
+
+interface UploadedPhoto {
+ file: File;
+ preview: string;
+ name: string;
+}
+
+interface PhotoUploadModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onPhotoUpload: (photo: PhotoData) => void;
+}
+
+const PhotoUploadModal: React.FC = ({
+ isOpen,
+ onClose,
+ onPhotoUpload,
+}) => {
+ const [dragActive, setDragActive] = useState(false);
+ const [uploadedPhoto, setUploadedPhoto] = useState(
+ null
+ );
+ const [photoName, setPhotoName] = useState("");
+ const [isUploading, setIsUploading] = useState(false);
+ const fileInputRef = useRef(null);
+
+ // Handle drag events
+ const handleDrag = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.type === "dragenter" || e.type === "dragover") {
+ setDragActive(true);
+ } else if (e.type === "dragleave") {
+ setDragActive(false);
+ }
+ };
+
+ // Handle dropped files
+ const handleDrop = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setDragActive(false);
+
+ if (e.dataTransfer.files && e.dataTransfer.files[0]) {
+ handleFile(e.dataTransfer.files[0]);
+ }
+ };
+
+ // Handle file selection
+ const handleFileSelect = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ handleFile(e.target.files[0]);
+ }
+ };
+
+ // Process the selected file
+ const handleFile = (file: File) => {
+ if (file.type.startsWith("image/")) {
+ const reader = new FileReader();
+ reader.onload = (e: ProgressEvent) => {
+ if (e.target && e.target.result) {
+ setUploadedPhoto({
+ file: file,
+ preview: e.target.result as string,
+ name: file.name,
+ });
+ setPhotoName(file.name.split(".")[0]); // Remove extension
+ }
+ };
+ reader.readAsDataURL(file);
+ } else {
+ alert("Please select an image file.");
+ }
+ };
+
+ // Handle form submission
+ const handleSubmit = async () => {
+ if (!uploadedPhoto || !photoName.trim()) return;
+
+ setIsUploading(true);
+
+ // Simulate upload delay
+ setTimeout(() => {
+ // Call the parent component's upload handler
+ onPhotoUpload({
+ id: Date.now(), // Generate a simple ID
+ name: photoName.trim(),
+ imageUrl: uploadedPhoto.preview, // In real app, this would be the uploaded URL
+ file: uploadedPhoto.file,
+ });
+
+ // Reset form
+ setUploadedPhoto(null);
+ setPhotoName("");
+ setIsUploading(false);
+ onClose();
+ }, 1500);
+ };
+
+ // Reset form when modal closes
+ const handleClose = () => {
+ setUploadedPhoto(null);
+ setPhotoName("");
+ setDragActive(false);
+ setIsUploading(false);
+ onClose();
+ };
+
+ // Handle backdrop click
+ const handleBackdropClick = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) {
+ handleClose();
+ }
+ };
+
+ if (!isOpen) return null;
+
+ return (
+
+
e.stopPropagation()}
+ className="bg-white border border-gray-700 rounded-xl mx-auto w-[30%]"
+ >
+ {/* Header */}
+
+
Upload Photo
+
+
+
+ {/* Content */}
+
+ {/* Drag and Drop Area */}
+
+ {uploadedPhoto ? (
+
+

+
+ {uploadedPhoto.file.name}
+
+
+
+ ) : (
+
+
📸
+
+
+ Drag and drop your photo here
+
+
or
+
+
+
+ )}
+
+
+
+
+ {/* Photo Name Input */}
+ {uploadedPhoto && (
+
+
+ setPhotoName(e.target.value)}
+ placeholder="Enter a name for this photo..."
+ className="w-full bg-white border border-gray-300 rounded-lg px-3 py-2 text-gray-800 placeholder-gray-400 focus:border-blue-400 focus:outline-none"
+ required
+ />
+
+ )}
+
+ {/* Action Buttons */}
+
+
+
+
+
+
+
+ );
+};
+
+export default PhotoUploadModal;
diff --git a/frontend/src/components/voiceInterface.tsx b/frontend/src/components/voiceInterface.tsx
new file mode 100644
index 000000000..fc0b3db8c
--- /dev/null
+++ b/frontend/src/components/voiceInterface.tsx
@@ -0,0 +1,309 @@
+"use client";
+
+import React, { useState, useRef, useEffect } from "react";
+
+interface VoiceInterfaceProps {
+ onTranscript: (transcript: string, isFinal: boolean) => void;
+ onVoiceStart: () => void;
+ onVoiceEnd: () => void;
+ onError: (error: string) => void;
+ disabled?: boolean;
+ // New props for external control
+ isListening?: boolean;
+ onToggleListening?: () => void;
+}
+
+const VoiceInterface: React.FC = ({
+ onTranscript,
+ onVoiceStart,
+ onVoiceEnd,
+ onError,
+ disabled = false,
+ isListening: externalIsListening,
+ onToggleListening,
+}) => {
+ const [internalIsListening, setInternalIsListening] = useState(false);
+ const [isSupported, setIsSupported] = useState(false);
+ const recognitionRef = useRef(null);
+
+ // Add accumulated transcript to handle pauses
+ const accumulatedTranscriptRef = useRef("");
+ const lastFinalTranscriptRef = useRef("");
+
+ // Use external control if provided, otherwise fall back to internal state
+ const isListening =
+ externalIsListening !== undefined
+ ? externalIsListening
+ : internalIsListening;
+ const controlled = externalIsListening !== undefined;
+
+ useEffect(() => {
+ // Check if speech recognition is supported
+ if (typeof window !== "undefined") {
+ const SpeechRecognitionClass =
+ window.SpeechRecognition || window.webkitSpeechRecognition;
+ if (SpeechRecognitionClass) {
+ setIsSupported(true);
+
+ const recognition = new SpeechRecognitionClass();
+ recognition.continuous = true;
+ recognition.interimResults = true;
+ recognition.lang = "en-US";
+
+ recognition.onstart = () => {
+ console.log("Speech recognition started");
+ // Reset accumulated transcript when starting
+ accumulatedTranscriptRef.current = "";
+ lastFinalTranscriptRef.current = "";
+
+ if (!controlled) {
+ setInternalIsListening(true);
+ }
+ onVoiceStart();
+ };
+
+ recognition.onresult = (event: SpeechRecognitionEvent) => {
+ let interimTranscript = "";
+ let finalTranscript = "";
+
+ // Process all results from the last processed index
+ for (let i = event.resultIndex; i < event.results.length; i++) {
+ const transcript = event.results[i][0].transcript;
+ if (event.results[i].isFinal) {
+ finalTranscript += transcript;
+ } else {
+ interimTranscript += transcript;
+ }
+ }
+
+ // Handle final results - accumulate them
+ if (finalTranscript) {
+ // Only add new final transcript (avoid duplicates)
+ if (finalTranscript !== lastFinalTranscriptRef.current) {
+ accumulatedTranscriptRef.current +=
+ (accumulatedTranscriptRef.current ? " " : "") +
+ finalTranscript.trim();
+ lastFinalTranscriptRef.current = finalTranscript;
+ }
+ }
+
+ // Combine accumulated final transcript with current interim
+ const fullTranscript =
+ accumulatedTranscriptRef.current +
+ (accumulatedTranscriptRef.current && interimTranscript ? " " : "") +
+ interimTranscript;
+
+ // Send the complete transcript (accumulated + interim)
+ if (fullTranscript.trim()) {
+ onTranscript(fullTranscript.trim(), !interimTranscript);
+ }
+ };
+
+ recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
+ console.error("Speech recognition error:", event.error);
+
+ // Handle "aborted" specifically - don't show as error to user
+ if (event.error === "aborted") {
+ console.log(
+ "Speech recognition was aborted - this is normal behavior"
+ );
+ if (!controlled) {
+ setInternalIsListening(false);
+ }
+ onVoiceEnd();
+ return;
+ }
+
+ // Handle "no-speech" - also normal, but restart recognition if still listening
+ if (event.error === "no-speech") {
+ console.log("No speech detected");
+ // If we're still supposed to be listening, restart recognition
+ if (isListening && !disabled) {
+ console.log("Restarting recognition due to no-speech...");
+ setTimeout(() => {
+ if (
+ recognitionRef.current &&
+ (controlled ? externalIsListening : internalIsListening)
+ ) {
+ try {
+ recognitionRef.current.start();
+ } catch (restartError) {
+ console.error(
+ "Failed to restart after no-speech:",
+ restartError
+ );
+ }
+ }
+ }, 100);
+ } else {
+ if (!controlled) {
+ setInternalIsListening(false);
+ }
+ onVoiceEnd();
+ }
+ return;
+ }
+
+ // Only show actual errors to user
+ if (!["aborted", "no-speech"].includes(event.error)) {
+ if (!controlled) {
+ setInternalIsListening(false);
+ }
+ onError(`Speech recognition error: ${event.error}`);
+ onVoiceEnd();
+ }
+ };
+
+ recognition.onend = () => {
+ console.log("Speech recognition ended");
+
+ // If we're still supposed to be listening, restart recognition
+ const shouldStillListen = controlled
+ ? externalIsListening
+ : internalIsListening;
+
+ if (shouldStillListen && !disabled) {
+ console.log(
+ "Recognition ended but should still be listening, restarting..."
+ );
+ setTimeout(() => {
+ if (recognitionRef.current && shouldStillListen) {
+ try {
+ recognitionRef.current.start();
+ } catch (restartError) {
+ console.error("Failed to restart recognition:", restartError);
+ if (!controlled) {
+ setInternalIsListening(false);
+ }
+ onVoiceEnd();
+ }
+ }
+ }, 100);
+ } else {
+ if (!controlled) {
+ setInternalIsListening(false);
+ }
+ onVoiceEnd();
+ }
+ };
+
+ recognitionRef.current = recognition;
+ } else {
+ setIsSupported(false);
+ }
+ }
+ }, [
+ onTranscript,
+ onVoiceStart,
+ onVoiceEnd,
+ onError,
+ controlled,
+ internalIsListening,
+ externalIsListening,
+ disabled,
+ ]);
+
+ // Effect to handle external listening state changes
+ useEffect(() => {
+ if (controlled && recognitionRef.current) {
+ if (externalIsListening && !internalIsListening) {
+ startListening();
+ } else if (!externalIsListening && internalIsListening) {
+ stopListening();
+ }
+ }
+ }, [externalIsListening, controlled, internalIsListening]);
+
+ const startListening = () => {
+ if (recognitionRef.current && !disabled) {
+ // Reset transcript accumulation
+ accumulatedTranscriptRef.current = "";
+ lastFinalTranscriptRef.current = "";
+
+ try {
+ setInternalIsListening(true);
+ recognitionRef.current.start();
+ } catch (error: any) {
+ console.error("Error starting speech recognition:", error);
+
+ // If recognition is already started, try to stop and restart
+ if (error.name === "InvalidStateError") {
+ console.log("Recognition already started, attempting restart...");
+ try {
+ recognitionRef.current.stop();
+ setTimeout(() => {
+ if (recognitionRef.current) {
+ recognitionRef.current.start();
+ }
+ }, 200);
+ } catch (restartError) {
+ console.error("Failed to restart recognition:", restartError);
+ setInternalIsListening(false);
+ onError("Failed to start speech recognition");
+ }
+ } else {
+ setInternalIsListening(false);
+ onError("Failed to start speech recognition");
+ }
+ }
+ }
+ };
+
+ const stopListening = () => {
+ if (recognitionRef.current) {
+ setInternalIsListening(false);
+ recognitionRef.current.stop();
+ }
+ };
+
+ const handleToggle = () => {
+ if (controlled && onToggleListening) {
+ onToggleListening();
+ } else {
+ // Uncontrolled mode
+ if (isListening) {
+ stopListening();
+ } else {
+ startListening();
+ }
+ }
+ };
+
+ if (!isSupported) {
+ return (
+
+
+ Speech recognition not supported in this browser
+
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+export default VoiceInterface;
diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts
new file mode 100644
index 000000000..beb6a9cbc
--- /dev/null
+++ b/frontend/src/services/apiClient.ts
@@ -0,0 +1,9 @@
+export async function callAgentAPI(userInput: string) {
+ const url = `${process.env.NEXT_PUBLIC_AGENT_API_URL}/react`;
+ const response = await fetch(url, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_input: userInput }),
+ });
+ return response.json();
+}
\ No newline at end of file
diff --git a/frontend/src/services/uploadService.ts b/frontend/src/services/uploadService.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/services/voiceService.ts b/frontend/src/services/voiceService.ts
new file mode 100644
index 000000000..2772c0cd9
--- /dev/null
+++ b/frontend/src/services/voiceService.ts
@@ -0,0 +1,392 @@
+export interface VoiceQuery {
+ query: string;
+ response: string;
+ memoryId: number;
+}
+
+export interface VoiceQueryResult {
+ success: boolean;
+ response?: string;
+ memoryId?: number;
+ error?: string;
+}
+
+export interface ConversationContext {
+ currentMemoryId?: number;
+ conversationHistory: VoiceQuery[];
+ memorySequenceIndex: { [memoryId: number]: number };
+}
+
+class VoiceService {
+ private demoConversation: VoiceQuery[] = [
+ {
+ query: "Who was that person I met at TechCrunch who worked on crypto payments?",
+ response: "That would be Jennifer Chen, VP of Engineering at Stripe! You met her at their booth on day 2 of TechCrunch conference. She mentioned they were hiring senior engineers for their crypto payments team.",
+ memoryId: 1
+ },
+ {
+ query: "Right! What else did we talk about?",
+ response: "You both bonded over being Stanford alumni, and she specifically mentioned their new payment APIs. You took a photo together at their booth, and she gave you her business card with instructions to reach out about the senior engineering role.",
+ memoryId: 1
+ },
+ {
+ query: "Did I ever follow up with her?",
+ response: "Yes, I reminded you on Tuesday morning as requested, and you sent her a LinkedIn message that same day. She responded within 2 hours suggesting a coffee meeting, which you scheduled for the next Friday. You met at Blue Bottle Coffee downtown and had what you described as a 'great conversation about team culture and technical challenges.",
+ memoryId: 1
+ },
+ {
+ query: "Perfect. I'm interviewing at Coinbase tomorrow and want to mention my connection to Stripe's crypto work. Can you give me some talking points?",
+ response: "Based on your conversation with Jennifer, here are some relevant points: • Stripe is actively investing in crypto infrastructure with dedicated teams • They're focusing on enterprise-grade payment solutions (Jennifer's emphasis) • Their approach prioritizes regulatory compliance and security • Jennifer mentioned they're seeing huge demand from fintech companies. This shows you're plugged into industry trends and have insights from a senior leader in the space. Would you like me to find other crypto-related conversations from your memory?",
+ memoryId: 1
+ },
+ {
+ query: "Tell me about Jake's birthday party",
+ response: "Jake's birthday party was last month at his apartment downtown. You brought homemade cupcakes and played board games until midnight. Sarah was there too, and you all had a great time catching up. Jake mentioned he's planning to move to Seattle for a new job opportunity.",
+ memoryId: 2
+ },
+ {
+ query: "What happened at the coffee meetup?",
+ response: "At the coffee meetup last week, you discussed the new startup ideas with Mike and Lisa. You sketched out some wireframes on napkins and talked about potential funding sources. Lisa offered to introduce you to her contact at Y Combinator.",
+ memoryId: 3
+ },
+ {
+ query: "Remind me about the team lunch",
+ response: "The team lunch was at that Italian restaurant near the office. Everyone celebrated Tom's promotion, and you discussed the Q4 roadmap. The pasta was excellent, and you all decided to make it a monthly tradition.",
+ memoryId: 4
+ },
+ {
+ query: "What was the project demo about?",
+ response: "The project demo showcased your new AI-powered analytics dashboard. The stakeholders were impressed with the real-time data visualization features. You received approval to move forward with the full implementation, and the VP mentioned it could be a game-changer for the company.",
+ memoryId: 5
+ }
+ ];
+
+ private conversationContext: ConversationContext = {
+ conversationHistory: [],
+ memorySequenceIndex: {}
+ };
+
+ /**
+ * Simulates processing a voice query and returns a response
+ * @param query - The transcribed text from speech
+ * @returns Promise with the response data
+ */
+ async processVoiceQuery(query: string): Promise {
+ try {
+ // Simulate network delay
+ await this.delay(500 + Math.random() * 1000);
+
+ // Check for topic triggers and get sequential response
+ const response = this.getSequentialResponse(query);
+
+ if (response) {
+ // Add to conversation history
+ this.conversationContext.conversationHistory.push(response);
+ this.conversationContext.currentMemoryId = response.memoryId;
+
+ return {
+ success: true,
+ response: response.response,
+ memoryId: response.memoryId
+ };
+ } else {
+ // Fallback response for unmatched queries
+ const fallbackResponse = this.generateFallbackResponse(query);
+ const fallbackQuery: VoiceQuery = {
+ query,
+ response: fallbackResponse,
+ memoryId: 0
+ };
+
+ this.conversationContext.conversationHistory.push(fallbackQuery);
+
+ return {
+ success: true,
+ response: fallbackResponse,
+ memoryId: 0
+ };
+ }
+ } catch (error) {
+ return {
+ success: false,
+ error: 'Failed to process voice query'
+ };
+ }
+ }
+
+ /**
+ * Get conversation history
+ * @returns Array of previous queries and responses
+ */
+ getConversationHistory(): VoiceQuery[] {
+ return this.conversationContext.conversationHistory;
+ }
+
+ /**
+ * Clear conversation history and reset sequence indices
+ */
+ clearConversationHistory(): void {
+ this.conversationContext = {
+ conversationHistory: [],
+ memorySequenceIndex: {}
+ };
+ }
+
+ /**
+ * Reset conversation (alias for clearConversationHistory for backward compatibility)
+ */
+ resetConversation(): void {
+ this.clearConversationHistory();
+ }
+
+
+ /**
+ * Get current memory context ID
+ */
+ getCurrentMemoryId(): number | undefined {
+ return this.conversationContext.currentMemoryId;
+ }
+
+ /**
+ * Check if the service is available (simulate health check)
+ */
+ async checkServiceHealth(): Promise {
+ try {
+ await this.delay(100);
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ /**
+ * Get sequential response based on exact match first, then topic triggers
+ * Returns the exact matching response or next response in sequence for the detected topic
+ */
+ private getSequentialResponse(query: string): VoiceQuery | null {
+ const normalizedQuery = query.toLowerCase().trim();
+
+ // First, try to find exact match
+ const exactMatch = this.demoConversation.find(
+ item => item.query.toLowerCase() === normalizedQuery
+ );
+
+ if (exactMatch) {
+ // Update sequence index to the position after this exact match
+ const memoryQueries = this.demoConversation.filter(
+ item => item.memoryId === exactMatch.memoryId
+ );
+ const exactMatchIndex = memoryQueries.findIndex(
+ item => item.query.toLowerCase() === normalizedQuery
+ );
+ if (exactMatchIndex !== -1) {
+ this.conversationContext.memorySequenceIndex[exactMatch.memoryId] = exactMatchIndex + 1;
+ }
+ return exactMatch;
+ }
+
+ // If no exact match, try fuzzy matching with high similarity
+ const fuzzyMatch = this.findFuzzyMatch(normalizedQuery);
+ if (fuzzyMatch) {
+ // Update sequence index to the position after this fuzzy match
+ const memoryQueries = this.demoConversation.filter(
+ item => item.memoryId === fuzzyMatch.memoryId
+ );
+ const fuzzyMatchIndex = memoryQueries.findIndex(
+ item => item.query === fuzzyMatch.query
+ );
+ if (fuzzyMatchIndex !== -1) {
+ this.conversationContext.memorySequenceIndex[fuzzyMatch.memoryId] = fuzzyMatchIndex + 1;
+ }
+ return fuzzyMatch;
+ }
+
+ // Define topic triggers and their corresponding memory IDs
+ const topicTriggers = {
+ techcrunch: 1,
+ stripe: 1,
+ crypto: 1,
+ jennifer: 1,
+ coinbase: 1,
+ jake: 2,
+ birthday: 2,
+ coffee: 3,
+ meetup: 3,
+ mike: 3,
+ lisa: 3,
+ lunch: 4,
+ team: 4,
+ italian: 4,
+ tom: 4,
+ demo: 5,
+ project: 5,
+ analytics: 5,
+ dashboard: 5
+ };
+
+ // Find which topic/memory this query relates to
+ let targetMemoryId: number | null = null;
+
+ for (const [trigger, memoryId] of Object.entries(topicTriggers)) {
+ if (normalizedQuery.includes(trigger)) {
+ targetMemoryId = memoryId;
+ break;
+ }
+ }
+
+ if (targetMemoryId === null) {
+ return null;
+ }
+
+ // Get all queries for this memory ID in order
+ const memoryQueries = this.demoConversation.filter(
+ item => item.memoryId === targetMemoryId
+ );
+
+ if (memoryQueries.length === 0) {
+ return null;
+ }
+
+ // Get current sequence index for this memory ID
+ const currentIndex = this.conversationContext.memorySequenceIndex[targetMemoryId] || 0;
+
+ // If we've reached the end of the sequence, start over
+ const sequenceIndex = currentIndex >= memoryQueries.length ? 0 : currentIndex;
+
+ // Update the sequence index for next time
+ this.conversationContext.memorySequenceIndex[targetMemoryId] = sequenceIndex + 1;
+
+ return memoryQueries[sequenceIndex];
+ }
+
+ /**
+ * Find fuzzy match for queries that are similar but not exactly the same
+ */
+ private findFuzzyMatch(query: string): VoiceQuery | null {
+ const queryWords = query.split(' ').filter(word => word.length > 2);
+ let bestMatch: VoiceQuery | null = null;
+ let highestScore = 0;
+ const minMatchThreshold = 0.7; // 70% similarity threshold
+
+ for (const item of this.demoConversation) {
+ const itemWords = item.query.toLowerCase().split(' ').filter(word => word.length > 2);
+ let matchedWords = 0;
+
+ for (const queryWord of queryWords) {
+ if (itemWords.some(itemWord =>
+ itemWord.includes(queryWord) ||
+ queryWord.includes(itemWord) ||
+ this.calculateSimilarity(queryWord, itemWord) > 0.8
+ )) {
+ matchedWords++;
+ }
+ }
+
+ const score = matchedWords / Math.max(queryWords.length, itemWords.length);
+
+ if (score > highestScore && score >= minMatchThreshold) {
+ highestScore = score;
+ bestMatch = item;
+ }
+ }
+
+ return bestMatch;
+ }
+
+ /**
+ * Calculate string similarity using Levenshtein distance
+ */
+ private calculateSimilarity(str1: string, str2: string): number {
+ const longer = str1.length > str2.length ? str1 : str2;
+ const shorter = str1.length > str2.length ? str2 : str1;
+
+ if (longer.length === 0) return 1.0;
+
+ const distance = this.levenshteinDistance(longer, shorter);
+ return (longer.length - distance) / longer.length;
+ }
+
+ /**
+ * Calculate Levenshtein distance between two strings
+ */
+ private levenshteinDistance(str1: string, str2: string): number {
+ const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
+
+ for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
+ for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
+
+ for (let j = 1; j <= str2.length; j++) {
+ for (let i = 1; i <= str1.length; i++) {
+ const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
+ matrix[j][i] = Math.min(
+ matrix[j][i - 1] + 1,
+ matrix[j - 1][i] + 1,
+ matrix[j - 1][i - 1] + indicator
+ );
+ }
+ }
+
+ return matrix[str2.length][str1.length];
+ }
+
+ /**
+ * Generate fallback response for unmatched queries
+ */
+ private generateFallbackResponse(query: string): string {
+ const fallbackResponses = [
+ "I don't have that specific information in my memory right now. Could you provide more context or ask about something else?",
+ "I'm not finding that in my records. Can you rephrase your question or ask about a different topic?",
+ "That doesn't match anything I remember. Try asking about recent meetings, events, or conversations.",
+ "I don't have details about that. Would you like to ask about something else from your recent activities?"
+ ];
+
+ return fallbackResponses[Math.floor(Math.random() * fallbackResponses.length)];
+ }
+
+ /**
+ * Utility method to simulate async delays
+ */
+ private delay(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ /**
+ * Get suggested queries (for UI hints)
+ */
+ getSuggestedQueries(): string[] {
+ return [
+ "Tell me about TechCrunch",
+ "What about Jake?",
+ "Tell me about the coffee meetup",
+ "Remind me about the team lunch",
+ "What was the project demo about?"
+ ];
+ }
+
+ /**
+ * Reset sequence for a specific memory ID
+ */
+ resetMemorySequence(memoryId: number): void {
+ this.conversationContext.memorySequenceIndex[memoryId] = 0;
+ }
+
+ /**
+ * Get current sequence index for a memory ID
+ */
+ getCurrentSequenceIndex(memoryId: number): number {
+ return this.conversationContext.memorySequenceIndex[memoryId] || 0;
+ }
+
+ /**
+ * Get queries by memory ID (for conversation grouping)
+ */
+ getQueriesByMemoryId(memoryId: number): VoiceQuery[] {
+ return this.demoConversation.filter(item => item.memoryId === memoryId);
+ }
+}
+
+// Export singleton instance
+export const voiceService = new VoiceService();
+
+// Export the class for testing purposes
+export default VoiceService;
\ No newline at end of file
diff --git a/frontend/src/services/websocketClient.ts b/frontend/src/services/websocketClient.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/utils/audioUtils.ts b/frontend/src/utils/audioUtils.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/utils/formatters.ts b/frontend/src/utils/formatters.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/utils/imageUtils.ts b/frontend/src/utils/imageUtils.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 000000000..c1334095f
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
new file mode 100755
index 000000000..8cb8bcc20
--- /dev/null
+++ b/scripts/deploy.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -e
+PROJECT_ID=$(gcloud config get-value project)
+REGION="us-central1"
+BACKEND_SERVICE="life-witness-agent-backend"
+FRONTEND_SERVICE="life-witness-agent-frontend"
+echo "Building backend Docker image..."
+gcloud builds submit --tag gcr.io/$PROJECT_ID/$BACKEND_SERVICE ./backend
+echo "Building frontend Docker image..."
+gcloud builds submit --tag gcr.io/$PROJECT_ID/$FRONTEND_SERVICE ./frontend
+echo "Deploying backend to Cloud Run..."
+gcloud run deploy $BACKEND_SERVICE \
+ --image gcr.io/$PROJECT_ID/$BACKEND_SERVICE \
+ --platform managed \
+ --region $REGION \
+ --allow-unauthenticated
+
+echo "Deploying frontend to Cloud Run..."
+gcloud run deploy $FRONTEND_SERVICE \
+ --image gcr.io/$PROJECT_ID/$FRONTEND_SERVICE \
+ --platform managed \
+ --region $REGION \
+ --allow-unauthenticated \
+ --memory 2Gi
+echo "Deployment complete!"
diff --git a/scripts/run_demo.sh b/scripts/run_demo.sh
new file mode 100755
index 000000000..2a8ba6e04
--- /dev/null
+++ b/scripts/run_demo.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+
+echo "Activating virtual environment..."
+source venv/bin/activate
+
+echo "Starting FastAPI demo app..."
+uvicorn ./backend/main:app --reload --host 0.0.0.0 --port 8080
diff --git a/scripts/setup_dev.sh b/scripts/setup_dev.sh
new file mode 100755
index 000000000..237bbbde0
--- /dev/null
+++ b/scripts/setup_dev.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+echo "Creating Python virtual environment..."
+python3 -m venv venv
+source venv/bin/activate
+
+echo "Upgrading pip..."
+pip install --upgrade pip
+
+echo "Installing requirements..."
+pip install -r ./backend/requirements.txt
+
+echo "Setup complete!"
diff --git a/tests/fixtures/mock_responses.py b/tests/fixtures/mock_responses.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/fixtures/sample_data.py b/tests/fixtures/sample_data.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/integration/test_agent_coordination.py b/tests/integration/test_agent_coordination.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/integration/test_api_endpoints.py b/tests/integration/test_api_endpoints.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/test_agents.py b/tests/unit/test_agents.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/test_orchestrator.py b/tests/unit/test_orchestrator.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/test_services.py b/tests/unit/test_services.py
new file mode 100644
index 000000000..e69de29bb