diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..593fb0a --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +AWS_REGION=your-region-here +AWS_ACCESS_KEY_ID=your-access-key-here +AWS_SECRET_ACCESS_KEY=your-secret-key-here \ No newline at end of file diff --git a/.gitignore b/.gitignore index c6bba59..a4b416e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,74 +1,47 @@ -# Logs -logs -*.log +# Environment variables +.env +.env.* +!.env.example + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Node +node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* -lerna-debug.log* .pnpm-debug.log* -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz +# IDE +.idea/ +.vscode/ +*.swp +*.swo +# Project specific +chunks.json +summary_cache.json +*-FINAL-REPORT.txt # Yarn Integrity file .yarn-integrity @@ -128,3 +101,17 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +# Test files +test.html +test.ts +test.js +test.d.ts +test.js.map +parallel-report.txt +quantum-deep-research-report.txt +quantum-parallel-report.txt +deepresearch-report.txt + +mcp-webresearch-original +TURTLE-SOUP.txt diff --git a/README.md b/README.md index 35dd7ab..4cd4bbc 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,46 @@ -# MCP Web Research Server +# MCP Deep Web Research Server (v0.3.0) -A Model Context Protocol (MCP) server for web research. +[![Node.js Version](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org/) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -Bring real-time info into Claude and easily research any topic. +A Model Context Protocol (MCP) server for advanced web research. + +## Latest Changes + +- Added visit_page tool for direct webpage content extraction +- Optimized performance to work within MCP timeout limits + * Reduced default maxDepth and maxBranching parameters + * Improved page loading efficiency + * Added timeout checks throughout the process + * Enhanced error handling for timeouts + +> This project is a fork of [mcp-webresearch](https://github.com/mzxrai/mcp-webresearch) by [mzxrai](https://github.com/mzxrai), enhanced with additional features for deep web research capabilities. We're grateful to the original creators for their foundational work. + +Bring real-time info into Claude with intelligent search queuing, enhanced content extraction, and deep research capabilities. ## Features -- Google search integration -- Webpage content extraction -- Research session tracking (list of visited pages, search queries, etc.) -- Screenshot capture +- Intelligent Search Queue System + - Batch search operations with rate limiting + - Queue management with progress tracking + - Error recovery and automatic retries + - Search result deduplication + +- Enhanced Content Extraction + - TF-IDF based relevance scoring + - Keyword proximity analysis + - Content section weighting + - Readability scoring + - Improved HTML structure parsing + - Structured data extraction + - Better content cleaning and formatting + +- Core Features + - Google search integration + - Webpage content extraction + - Research session tracking + - Markdown conversion with improved formatting ## Prerequisites @@ -18,42 +49,133 @@ Bring real-time info into Claude and easily research any topic. ## Installation -First, ensure you've downloaded and installed the [Claude Desktop app](https://claude.ai/download) and you have npm installed. +### Global Installation (Recommended) + +```bash +# Install globally using npm +npm install -g mcp-deepwebresearch + +# Or using yarn +yarn global add mcp-deepwebresearch -Next, add this entry to your `claude_desktop_config.json` (on Mac, found at `~/Library/Application\ Support/Claude/claude_desktop_config.json`): +# Or using pnpm +pnpm add -g mcp-deepwebresearch +``` + +### Local Project Installation + +```bash +# Using npm +npm install mcp-deepwebresearch + +# Using yarn +yarn add mcp-deepwebresearch + +# Using pnpm +pnpm add mcp-deepwebresearch +``` +### Claude Desktop Integration + +After installing the package, add this entry to your `claude_desktop_config.json`: + +#### Windows ```json { "mcpServers": { - "webresearch": { - "command": "npx", - "args": ["-y", "@mzxrai/mcp-webresearch@latest"] + "deepwebresearch": { + "command": "mcp-deepwebresearch", + "args": [] } } } ``` +Location: `%APPDATA%\Claude\claude_desktop_config.json` -This config allows Claude Desktop to automatically start the web research MCP server when needed. +#### macOS +```json +{ + "mcpServers": { + "deepwebresearch": { + "command": "mcp-deepwebresearch", + "args": [] + } + } +} +``` +Location: `~/Library/Application Support/Claude/claude_desktop_config.json` -## Usage +This config allows Claude Desktop to automatically start the web research MCP server when needed. -Simply start a chat with Claude and send a prompt that would benefit from web research. If you'd like a prebuilt prompt customized for deeper web research, you can use the `agentic-research` prompt that we provide through this package. Access that prompt in Claude Desktop by clicking the Paperclip icon in the chat input and then selecting `Choose an integration` → `webresearch` → `agentic-research`. +### First-time Setup -Example screenshot of web research +After installation, run this command to install required browser dependencies: +```bash +npx playwright install chromium +``` -### Tools +## Usage -1. `search_google` - - Performs Google searches and extracts results - - Arguments: `{ query: string }` +Simply start a chat with Claude and send a prompt that would benefit from web research. If you'd like a prebuilt prompt customized for deeper web research, you can use the `agentic-research` prompt that we provide through this package. Access that prompt in Claude Desktop by clicking the Paperclip icon in the chat input and then selecting `Choose an integration` → `deepwebresearch` → `agentic-research`. -2. `visit_page` - - Visits a webpage and extracts its content - - Arguments: `{ url: string, takeScreenshot?: boolean }` +### Tools -3. `take_screenshot` - - Takes a screenshot of the current page - - No arguments required +1. `deep_research` + - Performs comprehensive research with content analysis + - Arguments: + ```typescript + { + topic: string; + maxDepth?: number; // default: 2 + maxBranching?: number; // default: 3 + timeout?: number; // default: 55000 (55 seconds) + minRelevanceScore?: number; // default: 0.7 + } + ``` + - Returns: + ```typescript + { + findings: { + mainTopics: Array<{name: string, importance: number}>; + keyInsights: Array<{text: string, confidence: number}>; + sources: Array<{url: string, credibilityScore: number}>; + }; + progress: { + completedSteps: number; + totalSteps: number; + processedUrls: number; + }; + timing: { + started: string; + completed?: string; + duration?: number; + operations?: { + parallelSearch?: number; + deduplication?: number; + topResultsProcessing?: number; + remainingResultsProcessing?: number; + total?: number; + }; + }; + } + ``` + +2. `parallel_search` + - Performs multiple Google searches in parallel with intelligent queuing + - Arguments: `{ queries: string[], maxParallel?: number }` + - Note: maxParallel is limited to 5 to ensure reliable performance + +3. `visit_page` + - Visit a webpage and extract its content + - Arguments: `{ url: string }` + - Returns: + ```typescript + { + url: string; + title: string; + content: string; // Markdown formatted content + } + ``` ### Prompts @@ -65,39 +187,54 @@ A guided research prompt that helps Claude conduct thorough web research. The pr - Keep you informed and let you guide the research interactively - Always cite sources with URLs -### Resources +## Configuration Options -We expose two things as MCP resources: (1) captured webpage screenshots, and (2) the research session. +The server can be configured through environment variables: -#### Screenshots +- `MAX_PARALLEL_SEARCHES`: Maximum number of concurrent searches (default: 5) +- `SEARCH_DELAY_MS`: Delay between searches in milliseconds (default: 200) +- `MAX_RETRIES`: Number of retry attempts for failed requests (default: 3) +- `TIMEOUT_MS`: Request timeout in milliseconds (default: 55000) +- `LOG_LEVEL`: Logging level (default: 'info') -When you take a screenshot, it's saved as an MCP resource. You can access captured screenshots in Claude Desktop via the Paperclip icon. +## Error Handling -#### Research Session +### Common Issues -The server maintains a research session that includes: -- Search queries -- Visited pages -- Extracted content -- Screenshots -- Timestamps +1. Rate Limiting + - Symptom: "Too many requests" error + - Solution: Increase `SEARCH_DELAY_MS` or decrease `MAX_PARALLEL_SEARCHES` -### Suggestions +2. Network Timeouts + - Symptom: "Request timed out" error + - Solution: Ensure requests complete within the 60-second MCP timeout -For the best results, if you choose not to use the `agentic-research` prompt when doing your research, it may be helpful to suggest high-quality sources for Claude to use when researching general topics. For example, you could prompt `news today from reuters or AP` instead of `news today`. +3. Browser Issues + - Symptom: "Browser failed to launch" error + - Solution: Ensure Playwright is properly installed (`npx playwright install`) -## Problems +### Debugging -This is very much pre-alpha code. And it is also AIGC, so expect bugs. +This is beta software. If you run into issues: -If you run into issues, it may be helpful to check Claude Desktop's MCP logs: +1. Check Claude Desktop's MCP logs: + ```bash + # On macOS + tail -n 20 -f ~/Library/Logs/Claude/mcp*.log + + # On Windows + Get-Content -Path "$env:APPDATA\Claude\logs\mcp*.log" -Tail 20 -Wait + ``` -```bash -tail -n 20 -f ~/Library/Logs/Claude/mcp*.log -``` +2. Enable debug logging: + ```bash + export LOG_LEVEL=debug + ``` ## Development +### Setup + ```bash # Install dependencies pnpm install @@ -112,6 +249,56 @@ pnpm watch pnpm dev ``` +### Testing + +```bash +# Run all tests +pnpm test + +# Run tests in watch mode +pnpm test:watch + +# Run tests with coverage +pnpm test:coverage +``` + +### Code Quality + +```bash +# Run linter +pnpm lint + +# Fix linting issues +pnpm lint:fix + +# Type check +pnpm type-check +``` + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Coding Standards + +- Follow TypeScript best practices +- Maintain test coverage above 80% +- Document new features and APIs +- Update CHANGELOG.md for significant changes +- Follow semantic versioning + +### Performance Considerations + +- Use batch operations where possible +- Implement proper error handling and retries +- Consider memory usage with large datasets +- Cache results when appropriate +- Use streaming for large content + ## Requirements - Node.js >= 18 @@ -120,12 +307,17 @@ pnpm dev ## Verified Platforms - [x] macOS +- [x] Windows - [ ] Linux ## License MIT +## Credits + +This project builds upon the excellent work of [mcp-webresearch](https://github.com/mzxrai/mcp-webresearch) by [mzxrai](https://github.com/mzxrai). The original codebase provided the foundation for our enhanced features and capabilities. + ## Author -[mzxrai](https://github.com/mzxrai) \ No newline at end of file +[qpd-v](https://github.com/qpd-v) \ No newline at end of file diff --git a/chunk_document.py b/chunk_document.py new file mode 100644 index 0000000..9040671 --- /dev/null +++ b/chunk_document.py @@ -0,0 +1,25 @@ +from typing import List +import json + +def main(): + print("Requesting file chunking from claude-chunks MCP server...") + + # This will be replaced by the actual MCP tool call result + print("Please use the following MCP tool call:") + print(""" + +claude-chunks +chunk_file + +{ + "filePath": "amazon bedrock aws user guide.txt", + "maxTokens": 4000, + "overlapTokens": 200 +} + + +""") + print("\nAfter getting the chunks, save them to chunks.json and then run summarize_report.py") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/deepresearch-report.txt b/deepresearch-report.txt new file mode 100644 index 0000000..21e910c --- /dev/null +++ b/deepresearch-report.txt @@ -0,0 +1,48 @@ +Deep Research Report on LLM News + +Main Topics: +1. **Label** - Importance: 107.33 +2. **2409** - Importance: 74.82 +3. **17515** - Importance: 52.37 +4. **Arxiv** - Importance: 50.68 +5. **Toggle** - Importance: 48.63 +6. **Https** - Importance: 31.00 +7. **Org** - Importance: 5.52 + +Key Insights: +1. **Computer Science > Artificial Intelligence** + - arXiv:2409.17515 (cs) [Submitted on 26 Sep 2024 (v1), last revised 30 Oct 2024 (v3)] + - Title: From News to Forecast: Integrating Event Analysis in LLM-Based Time Series Forecasting with Reflection + - Authors: Xinlei Wang, Maike Feng, Jing Qiu, Jinjin Gu + +2. **LLM & Generative AI News** + - Meta’s big, expensive AI bet hinges on giving its models away for free + - Generative AI could soon decimate the call center industry, says CEO + - 5 Pro enters public preview on Vertex AI + +3. **Co-LLM Project** + - Co-LLM trains a general-purpose LLM to collaborate with expert models + - Used data like the BioASQ medical set to couple a base LLM with expert LLMs + +Sources: +1. [Computer Science > Artificial Intelligence](https://arxiv.org/abs/2409.17515) +2. [May 2024 Top LLM & Generative AI News, Research, & Open-Source Tools](https://odsc.medium.com/may-2024-top-llm-generative-ai-news-research-open-source-tools-0ad7f0b28f31) +3. [LLMs for innovation and technology intelligence: news categorization and trend signal detection](https://medium.com/mapegy-tech/llms-for-innovation-and-technology-intelligence-news-categorization-and-trend-signal-detection-ec4171627937) +4. [Enhancing LLM collaboration for smarter, more efficient solutions](https://news.mit.edu/2024/enhancing-llm-collaboration-smarter-more-efficient-solutions-0916) +5. [LLMs develop their own understanding of reality as their language abilities improve](https://news.mit.edu/2024/llms-develop-own-understanding-of-reality-as-language-abilities-improve-0814) +6. [Brain News Topics Analysis with LLM](https://braincompany.co/bntallm.html) +7. [From News to Forecast: Integrating Event Analysis in LLM-Based Time Series Forecasting with Reflection | OpenReview](https://openreview.net/forum?id=tj8nsfxi5r&referrer=%5Bthe%20profile%20of%20Jinjin%20Gu%5D(%2Fprofile%3Fid%3D~Jinjin_Gu1)) +8. [LLMs aren’t always bad at writing news headlines](https://sixcolors.com/post/2025/01/llms-arent-always-bad-at-writing-news-headlines/) +9. [Things we learned about LLMs in 2024 | Hacker News](https://news.ycombinator.com/item?id=42560558) +10. [News](https://www.infoq.com/llms/news/) +11. [AI and LLM News Articles (2023) - Health Research Alliance](https://www.healthra.org/resources/ai-and-llm-news-articles-2023/) +12. [What’s Currently Happening in LLMs? (Q2 2024)](https://www.startus-insights.com/innovators-guide/llm-news-brief/) +13. [Thomson Reuters CoCounsel Tests Custom LLM from OpenAI, Broadening its Multi-Model Product Strategy](https://www.prnewswire.com/news-releases/thomson-reuters-cocounsel-tests-custom-llm-from-openai-broadening-its-multi-model-product-strategy-302314877.html) +14. [Can AI Hold Consistent Values? Stanford Researchers Probe LLM Consistency and Bias](https://hai.stanford.edu/news/can-ai-hold-consistent-values-stanford-researchers-probe-llm-consistency-and-bias) +15. [We Built a News Site Powered by LLMs and Public Data: Here’s What We Learned](https://generative-ai-newsroom.com/we-built-a-news-site-powered-by-llms-and-public-data-heres-what-we-learned-aba6c52a7ee4) +16. [Extracting Structured Insights from Financial News: An Augmented LLM Driven Approach](https://arxiv.org/html/2407.15788v1) +17. [What would you like to report?](https://dl.acm.org/doi/10.1145/3677052.3698642) +18. [An Exploration of Large Language Models for Verification of News Headlines](https://ieeexplore.ieee.org/document/10411561/) +19. [AI and Large Language Models (LLM) - Health Research Alliance](https://www.healthra.org/communities/ai-and-large-language-models/) +20. [Can Language Models Really Understand? Study Uncovers Limits in AI Logic - Neuroscience News](https://neurosciencenews.com/llm-ai-logic-27987/) +21. [NVIDIA LLM News](https://www.nvidia.com/en-us/deep-learning-ai/large-language-model-news/) \ No newline at end of file diff --git a/document_processor.py b/document_processor.py new file mode 100644 index 0000000..3ac80b3 --- /dev/null +++ b/document_processor.py @@ -0,0 +1,691 @@ +import os +import json +import re +import time +import glob +import math +import hashlib +from typing import List, Dict, Tuple +from datetime import datetime +import boto3 +from dotenv import load_dotenv +from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED +from tqdm import tqdm +from botocore.exceptions import ClientError +from rate_tracker import RateTracker, RateTrackerConfig + +# Load environment variables +load_dotenv() + +class Cache: + def __init__(self, cache_file: str = 'summary_cache.json'): + self.cache_file = cache_file + self.cache = self._load_cache() + + def _load_cache(self) -> Dict: + if os.path.exists(self.cache_file): + try: + with open(self.cache_file, 'r') as f: + return json.load(f) + except: + return {} + return {} + + def _save_cache(self): + with open(self.cache_file, 'w') as f: + json.dump(self.cache, f) + + def get(self, key: str) -> str: + return self.cache.get(key) + + def set(self, key: str, value: str): + self.cache[key] = value + self._save_cache() + +class DocumentChunker: + def __init__(self, target_chunk_size: int = 6000): + self.target_chunk_size = target_chunk_size + self.header_pattern = re.compile(r'^(?:#{1,6}|\d+\.)\s+.+$', re.MULTILINE) + + def split_into_sections(self, text: str) -> List[str]: + sections = [] + current_section = [] + + for line in text.split('\n'): + if self.header_pattern.match(line) and len('\n'.join(current_section).split()) > self.target_chunk_size // 2: + if current_section: + sections.append('\n'.join(current_section)) + current_section = [line] + else: + current_section.append(line) + + if current_section: + sections.append('\n'.join(current_section)) + + return sections + + def optimize_chunks(self, sections: List[str]) -> List[str]: + optimized_chunks = [] + current_chunk = [] + current_size = 0 + + for section in sections: + section_size = len(section.split()) + + if section_size > self.target_chunk_size: + sentences = re.split(r'(?<=[.!?])\s+', section) + temp_chunk = [] + temp_size = 0 + + for sentence in sentences: + sentence_size = len(sentence.split()) + if temp_size + sentence_size <= self.target_chunk_size: + temp_chunk.append(sentence) + temp_size += sentence_size + else: + if temp_chunk: + optimized_chunks.append(' '.join(temp_chunk)) + temp_chunk = [sentence] + temp_size = sentence_size + + if temp_chunk: + optimized_chunks.append(' '.join(temp_chunk)) + + elif current_size + section_size <= self.target_chunk_size: + current_chunk.append(section) + current_size += section_size + else: + if current_chunk: + optimized_chunks.append('\n\n'.join(current_chunk)) + current_chunk = [section] + current_size = section_size + + if current_chunk: + optimized_chunks.append('\n\n'.join(current_chunk)) + + return optimized_chunks + + def chunk_document(self, text: str) -> List[str]: + sections = self.split_into_sections(text) + chunks = self.optimize_chunks(sections) + return chunks + +class BedrockSummarizer: + def __init__(self, topic: str, max_workers: int = 4): + self.topic = topic + self.bedrock = boto3.client( + service_name='bedrock-runtime', + region_name=os.getenv('AWS_REGION'), + aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), + aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY') + ) + self.model_id = "anthropic.claude-3-5-sonnet-20241022-v2:0" + self.max_tokens = 4000 + self.temperature = 0.7 + self.max_workers = max_workers + self.cache = Cache() + self.rate_tracker = RateTracker(RateTrackerConfig( + requestsPerMinute=50, # AWS Bedrock limit + tokensPerMinute=400000, # AWS Bedrock limit + maxTokensPerRequest=4000 + )) + + def get_cache_key(self, content: str, task: str) -> str: + content_hash = hashlib.md5(content.encode()).hexdigest() + return f"{content_hash}_{task}" + + def wait_for_rate_limit(self, estimated_tokens: int): + wait_time = self.rate_tracker.get_wait_time(estimated_tokens) + if wait_time > 0: + status = self.rate_tracker.get_rate_limit_status() + print(f"[RATE] Waiting {wait_time}ms for capacity") + print(f"[RATE] Requests: {status['requestsUsed']}/{self.rate_tracker.config['requestsPerMinute']}") + print(f"[RATE] Tokens: {status['tokensUsed']}/{self.rate_tracker.config['tokensPerMinute']}") + print(f"[RATE] Next reset in: {math.ceil((status['nextReset'] - time.time()) * 1000)}ms") + time.sleep(wait_time / 1000) # Convert ms to seconds + + def determine_analysis_tasks(self, content: str) -> Dict[str, str]: + """Analyze content to determine appropriate analysis tasks.""" + prompt = f"""Analyze this content about {self.topic} and create a comprehensive analysis plan. The analysis should follow a clear narrative structure with the following sections: + + 1. Executive Summary + - Brief overview of key findings and main points + - High-level insights and conclusions + - Critical takeaways for readers + + 2. Main Analysis Sections (3-4 key areas) + - Each section should have a clear focus + - Include supporting evidence and examples + - Build a coherent narrative flow + + 3. Supporting Analysis + - Technical details and implementation specifics + - Market or domain-specific considerations + - Risk and opportunity analysis + + 4. Future Implications + - Long-term impact and consequences + - Emerging trends and developments + - Recommendations and next steps + + Format your response as a JSON object where each key represents a section name and its value describes what should be analyzed. The sections should flow logically and build upon each other. + + Example format: + {{ + "executive_summary": "comprehensive overview of key findings and critical insights", + "technical_foundation": "detailed analysis of core technologies and methodologies", + "market_dynamics": "examination of market forces, competition, and opportunities", + "implementation_strategy": "practical considerations and execution framework", + "future_outlook": "long-term implications and strategic recommendations" + }} + + Content for analysis: + {content[:2000]} + + Create a structure that tells a cohesive story while maintaining analytical rigor. + """ + + response = self.invoke_model_with_retry({ + "anthropic_version": "bedrock-2023-05-31", + "max_tokens": 4000, + "messages": [{"role": "user", "content": prompt}], + "temperature": 0.7 + }) + + try: + # Extract JSON from response + json_str = response[response.find('{'):response.rfind('}')+1] + tasks = json.loads(json_str) + return tasks + except: + # Fallback to default tasks if JSON parsing fails + return { + "overview": f"main aspects and key concepts of {self.topic}", + "analysis": f"detailed analysis of important elements in {self.topic}", + "implications": f"effects and implications of {self.topic}", + "future": f"future trends and developments in {self.topic}" + } + + def create_prompt(self, content: str, task: str, is_final: bool = False) -> dict: + if is_final: + prompt = f"""Create a comprehensive final analysis about {self.topic} focusing on {task}. + Use these previous summaries to create a well-structured narrative that flows naturally: + + {content} + + Guidelines for the analysis: + 1. Start with a clear introductory paragraph that sets the context + 2. Develop your main points using a mix of: + - Well-crafted paragraphs that explain key concepts + - Specific examples and evidence to support claims + - Selective use of bullet points for lists or features + 3. Use clear transitions between major points + 4. Include relevant data or metrics where appropriate + 5. End with a strong concluding paragraph that synthesizes the main insights + + Focus on creating a cohesive narrative that a reader can follow easily. Avoid excessive bullet points or lists unless absolutely necessary. Each major point should flow naturally into the next.""" + else: + prompt = f"""Analyze this portion of content about {self.topic}, focusing on {task}. + Create a well-structured analysis that will contribute to the larger narrative: + + {content} + + Guidelines for analysis: + 1. Begin with a clear topic sentence that connects to the main theme + 2. Develop your analysis using: + - Well-written paragraphs that explain concepts + - Specific examples from the content + - Data or evidence when available + 3. Make connections between different points + 4. Highlight particularly significant findings + 5. End with a summary that ties back to the main focus + + Write in a clear, engaging style that maintains analytical rigor while being accessible to readers. + Use paragraphs as your primary structure, with lists only when they truly add value.""" + + return { + "anthropic_version": "bedrock-2023-05-31", + "max_tokens": self.max_tokens, + "messages": [{"role": "user", "content": prompt}], + "temperature": self.temperature + } + + def invoke_model_with_retry(self, prompt: dict, max_retries: int = 5, timeout: int = 30) -> str: + retry_count = 0 + base_delay = 1 + + # Estimate token usage + prompt_tokens = len(prompt['messages'][0]['content'].split()) + max_response_tokens = prompt.get('max_tokens', 4000) + estimated_total_tokens = prompt_tokens + max_response_tokens + + print(f"[TOKENS] Estimated input tokens: {prompt_tokens}") + print(f"[TOKENS] Maximum response tokens: {max_response_tokens}") + print(f"[TOKENS] Estimated total tokens: {estimated_total_tokens}") + + while retry_count < max_retries: + try: + print("[API] Checking rate limits...") + self.wait_for_rate_limit(estimated_total_tokens) + print(f"[API] Sending request to Bedrock (timeout: {timeout}s)...") + + # Set a timeout for the API call + start_time = time.time() + try: + response = self.bedrock.invoke_model( + modelId=self.model_id, + body=json.dumps(prompt) + ) + + # Check if we exceeded the timeout + if time.time() - start_time > timeout: + print(f"[TIMEOUT] API call exceeded {timeout} seconds") + raise TimeoutError(f"API call timed out after {timeout} seconds") + + print("[API] Reading response from Bedrock...") + response_data = response.get('body').read() + print(f"[DEBUG] Raw response size: {len(response_data)} bytes") + + try: + response_body = json.loads(response_data) + print("[DEBUG] Response structure:", json.dumps(response_body, indent=2)[:500] + "...") + + if 'content' not in response_body: + print("[ERROR] Missing 'content' in response") + print("[DEBUG] Full response:", response_body) + raise ValueError("Invalid response structure - missing 'content'") + + if not response_body['content'] or not isinstance(response_body['content'], list): + print("[ERROR] Invalid 'content' structure in response") + print("[DEBUG] Content value:", response_body['content']) + raise ValueError("Invalid content structure") + + if 'text' not in response_body['content'][0]: + print("[ERROR] Missing 'text' in content") + print("[DEBUG] Content object:", response_body['content'][0]) + raise ValueError("Invalid content structure - missing 'text'") + + response_text = response_body['content'][0]['text'] + + # Record actual token usage + actual_response_tokens = len(response_text.split()) + actual_total_tokens = prompt_tokens + actual_response_tokens + print(f"[TOKENS] Actual response tokens: {actual_response_tokens}") + print(f"[TOKENS] Actual total tokens: {actual_total_tokens}") + self.rate_tracker.record_usage(actual_total_tokens) + + print("[OK] Successfully processed API response") + return response_text + + except json.JSONDecodeError as e: + print("[ERROR] Failed to parse JSON response") + print(f"[DEBUG] JSON Error: {str(e)}") + print("[DEBUG] Raw response:", response_data) + raise + + except Exception as e: + print(f"[ERROR] API call failed: {str(e)}") + print("[DEBUG] Error type:", type(e).__name__) + raise + except (ClientError, TimeoutError) as e: + retry_count += 1 + if retry_count == max_retries: + print(f"[ERROR] Failed after {max_retries} retries: {str(e)}") + return "" + + if isinstance(e, ClientError): + error_code = e.response['Error']['Code'] + if error_code in ['ThrottlingException', 'ServiceUnavailableException']: + delay = base_delay * (2 ** retry_count) + print(f"[WARN] Rate limited, attempt {retry_count}/{max_retries}") + print(f"[WAIT] Waiting {delay} seconds before retry") + time.sleep(delay) + else: + print(f"[ERROR] Bedrock API error: {error_code} - {str(e)}") + return "" + else: # TimeoutError + delay = base_delay * (2 ** retry_count) + print(f"[RETRY] Request timed out, attempt {retry_count}/{max_retries}") + print(f"[WAIT] Waiting {delay} seconds before retry") + time.sleep(delay) + + except Exception as e: + print(f"[ERROR] Unexpected error: {str(e)}") + print("[DEBUG] Error type:", type(e).__name__) + print("[DEBUG] Error details:", str(e)) + return "" + + def process_chunk(self, args: Tuple[str, str, int, int]) -> Tuple[int, str]: + chunk, task, chunk_idx, total_chunks = args + start_time = time.time() + print(f"\n[INFO] Processing chunk {chunk_idx + 1}/{total_chunks}") + print(f"[INFO] Task: {task}") + print(f"[INFO] Chunk size: {len(chunk.split())} words") + + cache_key = self.get_cache_key(chunk, task) + cached_result = self.cache.get(cache_key) + if cached_result: + print(f"[CACHE] Using cached result for chunk {chunk_idx + 1}") + print(f"[PROGRESS] Status: Retrieved from cache") + return chunk_idx, cached_result + + print(f"[API] Creating prompt for chunk {chunk_idx + 1}") + prompt = self.create_prompt(chunk, task) + prompt_tokens = len(prompt['messages'][0]['content'].split()) + print(f"[API] Prompt size: {prompt_tokens} tokens") + + print(f"[API] Calling Bedrock API for chunk {chunk_idx + 1}") + result = self.invoke_model_with_retry(prompt) + + elapsed_time = time.time() - start_time + if result: + result_tokens = len(result.split()) + print(f"[OK] Successfully processed chunk {chunk_idx + 1} in {elapsed_time:.1f}s") + print(f"[INFO] Response size: {result_tokens} tokens") + print(f"[CACHE] Caching result for chunk {chunk_idx + 1}") + self.cache.set(cache_key, result) + print(f"[PROGRESS] Status: Completed successfully") + else: + print(f"[ERROR] Failed to process chunk {chunk_idx + 1} after {elapsed_time:.1f}s") + print(f"[PROGRESS] Status: Failed") + + return chunk_idx, result + + def process_chunks_for_task(self, chunks: List[str], task: str, timeout: int = 600) -> List[str]: + """Process chunks with timeout and better progress tracking.""" + print(f"\nProcessing {len(chunks)} chunks for task: {task}") + chunk_results = [None] * len(chunks) + start_time = time.time() + + with ThreadPoolExecutor(max_workers=self.max_workers) as executor: + print(f"Using {self.max_workers} parallel workers") + + # Submit all tasks and track them + futures_to_index = { + executor.submit(self.process_chunk, (chunk, task, i, len(chunks))): i + for i, chunk in enumerate(chunks) + } + + pending = set(futures_to_index.keys()) + completed_chunks = 0 + + while pending: + # Check overall timeout + if time.time() - start_time > timeout: + print(f"\n[TIMEOUT] Task processing exceeded {timeout} seconds") + print(f"[STATUS] Completed {completed_chunks}/{len(chunks)} chunks") + break + + # Wait for the next chunk to complete with a timeout + done, pending = wait(pending, timeout=30, return_when=FIRST_COMPLETED) + + # Process completed chunks + for future in done: + try: + idx, result = future.result(timeout=1) + if result: + chunk_results[idx] = result + completed_chunks += 1 + print(f"[OK] Chunk {idx + 1}/{len(chunks)} processed ({completed_chunks} total complete)") + print(f"[PROGRESS] {completed_chunks}/{len(chunks)} chunks completed") + print(f"[TIME] Elapsed: {int(time.time() - start_time)}s") + else: + print(f"[FAIL] Failed to process chunk {idx + 1}/{len(chunks)}") + except Exception as e: + print(f"[ERROR] Error processing chunk {futures_to_index[future] + 1}: {str(e)}") + continue + + # Print status update for remaining chunks + if pending: + remaining_time = timeout - (time.time() - start_time) + print(f"\n[STATUS] {len(pending)} chunks remaining") + print(f"[TIME] Timeout in {int(remaining_time)}s") + + # Cancel any remaining futures + for future in pending: + future.cancel() + print(f"[CANCEL] Cancelled chunk {futures_to_index[future] + 1}") + + successful_chunks = [r for r in chunk_results if r is not None] + print(f"\nTask {task}: {len(successful_chunks)}/{len(chunks)} chunks processed successfully") + return successful_chunks + + def process_content(self, chunks: List[str], task_timeout: int = 900) -> Dict[str, str]: + """Process content with task-level timeouts and progress tracking.""" + print("\nAnalyzing content to determine appropriate tasks...") + tasks = self.determine_analysis_tasks(chunks[0]) + print(f"\nIdentified {len(tasks)} analysis tasks:") + for task_name, task_desc in tasks.items(): + print(f"- {task_name}: {task_desc}") + + results = {} + print("\nInitializing parallel task processing...") + start_time = time.time() + + with ThreadPoolExecutor(max_workers=len(tasks)) as executor: + # Submit all tasks with chunk-level timeout of 600s + future_to_task = { + executor.submit(self.process_chunks_for_task, chunks, task_desc, 600): task_name + for task_name, task_desc in tasks.items() + } + + pending = set(future_to_task.keys()) + completed_tasks = 0 + + while pending: + # Check overall timeout + elapsed = time.time() - start_time + if elapsed > task_timeout: + print(f"\n[TIMEOUT] Overall processing exceeded {task_timeout} seconds") + print(f"[STATUS] Completed {completed_tasks}/{len(tasks)} tasks") + break + + # Wait for the next task to complete + try: + done, pending = wait(pending, timeout=30, return_when=FIRST_COMPLETED) + + # Process completed tasks + for future in done: + task_name = future_to_task[future] + try: + print(f"\n[TASK] Processing {task_name} ({completed_tasks + 1}/{len(tasks)})") + chunk_summaries = future.result(timeout=30) + + if chunk_summaries: + print(f"[INFO] Creating final summary for {task_name}") + print(f"[INFO] Processing {len(chunk_summaries)} chunk summaries") + results[task_name] = self.create_final_summary(chunk_summaries, tasks[task_name]) + print(f"[OK] Summary completed for {task_name}") + else: + print(f"[WARN] No valid summaries generated for {task_name}") + results[task_name] = f"Error: No valid summaries generated for {task_name}" + + completed_tasks += 1 + elapsed = time.time() - start_time + print(f"[PROGRESS] {completed_tasks}/{len(tasks)} tasks completed") + print(f"[TIME] Total elapsed: {int(elapsed)}s") + + except Exception as e: + print(f"[ERROR] Error processing task {task_name}: {str(e)}") + print(f"[DEBUG] Error type: {type(e).__name__}") + results[task_name] = f"Error processing task: {str(e)}" + completed_tasks += 1 + + # Print status for remaining tasks + if pending: + remaining_time = task_timeout - elapsed + print(f"\n[STATUS] {len(pending)} tasks remaining") + print(f"[TIME] Overall timeout in {int(remaining_time)}s") + print("[TASKS] Remaining:", ", ".join(future_to_task[f] for f in pending)) + + except Exception as e: + print(f"[ERROR] Error in task processing loop: {str(e)}") + print("[DEBUG] Error type:", type(e).__name__) + continue + + # Cancel any remaining tasks + for future in pending: + future.cancel() + task_name = future_to_task[future] + print(f"[CANCEL] Cancelled task: {task_name}") + if task_name not in results: + results[task_name] = "Error: Task cancelled due to timeout" + + print(f"\n[COMPLETE] Processed {completed_tasks}/{len(tasks)} tasks") + print(f"[TIME] Total processing time: {int(time.time() - start_time)}s") + + return results, tasks + + def create_final_summary(self, chunk_summaries: List[str], task: str) -> str: + print(f"\nCreating final summary for task: {task}") + print(f"Combining {len(chunk_summaries)} chunk summaries...") + combined_summaries = "\n\n".join(chunk_summaries) + + cache_key = self.get_cache_key(combined_summaries, f"final_{task}") + cached_result = self.cache.get(cache_key) + if cached_result: + print("[CACHE] Using cached summary") + return cached_result + + print("[INFO] Generating new summary...") + prompt = self.create_prompt(combined_summaries, task, is_final=True) + result = self.invoke_model_with_retry(prompt) + + if result: + print("[OK] Summary generated successfully") + print("[INFO] Caching result for future use...") + self.cache.set(cache_key, result) + else: + print("[ERROR] Failed to generate summary") + + return result + +def main(): + import sys + if len(sys.argv) < 2: + print("Usage: python document_processor.py ") + sys.exit(1) + + topic = sys.argv[1] + # Find the most recent summary file for the topic + summary_pattern = f"*{topic.lower().replace(' ', '-')}*summary.txt" + input_file = max(glob.glob(summary_pattern), key=os.path.getctime) + if not input_file: + print(f"Error: No summary file found matching pattern: {summary_pattern}") + sys.exit(1) + + # Check for required environment variables + required_env_vars = ['AWS_REGION', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'] + missing_vars = [var for var in required_env_vars if not os.getenv(var)] + if missing_vars: + print("Error: Missing required environment variables:", missing_vars) + print("Please set these variables in your .env file") + sys.exit(1) + + # Initialize chunker and summarizer + chunker = DocumentChunker(target_chunk_size=6000) + summarizer = BedrockSummarizer(topic, max_workers=4) + + # Read and chunk the document + print("Reading and chunking document...") + with open(input_file, 'r', encoding='utf-8') as f: + content = f.read() + + chunks = chunker.chunk_document(content) + print(f"Document split into {len(chunks)} chunks") + + # Save chunks for inspection if needed + with open('chunks.json', 'w', encoding='utf-8') as f: + json.dump({'chunks': chunks}, f, indent=2) + print("Chunks saved to chunks.json") + + # Process chunks + results, tasks = summarizer.process_content(chunks) + + # Generate final report with improved structure + sections = [] + + # Add report header with metadata + report_header = f"""# {topic} Analysis +Generated using AWS Bedrock Claude 3.5 Sonnet +Date: {datetime.now().strftime('%B %d, %Y')} + +---""" + sections.append(report_header) + + # Add executive summary section + if "executive_summary" in results: + sections.append(f"""## Executive Summary + +{results['executive_summary']} + +---""") + else: + # Create a synthetic executive summary from other sections + summary_content = [] + for task_name, result in results.items(): + # Take first paragraph from each section + first_para = result.split('\n\n')[0] if result else "" + if first_para: + summary_content.append(first_para) + + sections.append(f"""## Executive Summary + +{' '.join(summary_content[:2])} + +---""") + + # Add main content sections with transitions + main_sections = [] + previous_section = None + for task_name, task_desc in tasks.items(): + if task_name != "executive_summary": # Skip executive summary as it's already added + section_content = results[task_name] + + # Add transition from previous section if applicable + transition = "" + if previous_section: + transition = f"Building on the {previous_section} discussed above, we now turn our attention to {task_name}.\n\n" + + # Add section with description and content + # Handle task_desc which could be a string or dict value + description = task_desc + if isinstance(task_desc, dict): + description = task_desc.get(task_name, '') + + formatted_desc = description.capitalize() if isinstance(description, str) else '' + + main_sections.append(f"""## {task_name.title()} +_{formatted_desc}_ + +{transition}{section_content} + +---""") + previous_section = task_name + + # Add main sections to report + sections.extend(main_sections) + + # Add recommendations section + sections.append("""## Next Steps and Recommendations + +Based on the comprehensive analysis presented above, consider the following key recommendations: + +1. Review and prioritize the insights from each section +2. Evaluate the implications for your specific context +3. Develop an action plan based on the findings +4. Monitor ongoing developments in this rapidly evolving field + +For detailed information about any aspect of this analysis, refer to the relevant sections above.""") + + # Combine all sections into final report + final_report = f"{chr(10).join(sections)}" + + # Write final report + output_file = f"{topic.lower().replace(' ', '-')}-FINAL-REPORT.txt" + with open(output_file, 'w', encoding='utf-8') as f: + f.write(final_report) + print(f"\nFinal report has been generated: {output_file}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/improvements-plan.txt b/improvements-plan.txt new file mode 100644 index 0000000..e16d29d --- /dev/null +++ b/improvements-plan.txt @@ -0,0 +1,85 @@ +# MCP-WebResearch Improvements Plan + +## Phase 1: High Priority Improvements + +### 1. Intelligent Search Queue System [IN PROGRESS] +Implementation Steps: +1. Create SearchQueue class to manage search operations + - Add queue data structure for pending searches + - Implement rate limiting with exponential backoff + - Add progress tracking and status reporting + - Handle error recovery and retries + +2. Add new tool endpoints: + - batch_search: Queue multiple searches + - get_queue_status: Check search queue progress + - cancel_search: Cancel pending searches + +3. Enhance search results aggregation: + - Implement result deduplication + - Add result sorting options + - Improve error handling and recovery + +4. Add queue persistence: + - Save queue state between sessions + - Handle interrupted searches + - Implement queue recovery + +Testing Criteria: +- Queue should handle at least 50 searches without triggering anti-bot measures +- Rate limiting should adapt to Google's response patterns +- Progress updates should be accurate and timely +- Results should be properly aggregated and deduplicated + +### 2. Enhanced Content Extraction & Relevance Scoring [IN PROGRESS] +Implementation Steps: +1. Improve content relevance scoring: + - Implement TF-IDF scoring + - Add keyword proximity analysis + - Add content section weighting + - Implement readability scoring + +2. Enhance content extraction: + - Improve HTML structure parsing + - Add support for common content patterns + - Implement better content cleaning + - Add structured data extraction + +3. Add content summarization: + - Implement extractive summarization + - Add key points extraction + - Generate section summaries + - Preserve important metadata + +4. Improve markdown conversion: + - Enhance formatting preservation + - Better handle tables and lists + - Improve code block handling + - Better preserve document structure + +Testing Criteria: +- Content relevance scores should align with human judgment +- Extracted content should be clean and well-formatted +- Structured data should be accurately identified +- Summaries should capture key information +- Markdown output should be consistently formatted + +## Implementation Notes: +- Each feature will be implemented incrementally +- Testing will be done after each major component +- Code reviews required before merging +- Performance benchmarks will be maintained + +## Status Tracking: +[ ] Feature 1 Started +[ ] Feature 1 Tested +[ ] Feature 1 Complete +[ ] Feature 2 Started +[ ] Feature 2 Tested +[ ] Feature 2 Complete + +## Dependencies to Add: +- tf-idf-search (for relevance scoring) +- readability (for content analysis) +- html-to-md (for improved markdown conversion) +- rate-limiter-flexible (for queue management) \ No newline at end of file diff --git a/index.ts b/index.ts index d78079c..43c6676 100644 --- a/index.ts +++ b/index.ts @@ -25,6 +25,11 @@ import type { Node } from "turndown"; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; +import { ParallelSearch } from './src/parallel-search.js'; +import { ParallelSearchResult } from './src/types.js'; + +// Initialize parallel search instance +const parallelSearch = new ParallelSearch(); // Initialize temp directory for screenshots const SCREENSHOTS_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-screenshots-')); @@ -141,6 +146,35 @@ const TOOLS: Tool[] = [ required: ["query"], }, }, + { + name: "parallel_search", + description: "Perform multiple Google searches in parallel", + inputSchema: { + type: "object", + properties: { + queries: { + type: "array", + items: { type: "string" }, + description: "Array of search queries to execute in parallel" + }, + maxParallel: { + type: "number", + description: "Maximum number of parallel searches (default: 10, max: 10)", + minimum: 1, + maximum: 10 + }, + outputDir: { + type: "string", + description: "Directory to save search results (default: search-results)" + }, + includeTimings: { + type: "boolean", + description: "Include execution time information in results (default: false)" + } + }, + required: ["queries"] + }, + }, { name: "visit_page", description: "Visit a webpage and extract its content", @@ -1112,6 +1146,98 @@ server.setRequestHandler(CallToolRequestSchema, async (request): Promise !r.error) + .forEach(result => { + result.results.forEach(searchResult => { + addResult({ + url: searchResult.url, + title: searchResult.title, + content: searchResult.snippet, + timestamp: new Date().toISOString(), + }); + }); + }); + + return { + content: [{ + type: "text", + text: JSON.stringify({ + summary: { + ...searchResult.summary, + outputDirectory: options.outputDir, + ...(includeTimings ? { + totalExecutionTime: totalTime, + averageExecutionTime: Math.round(totalTime / queries.length) + } : {}) + }, + results: searchResult.results.map(r => ({ + searchId: r.searchId, + query: r.query, + status: r.error ? 'failed' : 'success', + resultCount: r.results.length, + error: r.error, + results: r.results, + ...(includeTimings ? { executionTime: r.executionTime } : {}) + })) + }, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: "text", + text: `Failed to execute parallel search: ${(error as Error).message}` + }], + isError: true + }; + } + } + // Handle unknown tool requests default: throw new McpError( diff --git a/package.json b/package.json index 264eb18..620b4c6 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,70 @@ { - "name": "@mzxrai/mcp-webresearch", - "version": "0.1.7", - "description": "MCP server for web research", - "license": "MIT", - "author": "mzxrai", - "homepage": "https://github.com/mzxrai/mcp-webresearch", - "bugs": "https://github.com/mzxrai/mcp-webresearch/issues", + "name": "mcp-deepwebresearch", + "version": "0.3.0", + "description": "MCP Web Research Server with Deep Research capabilities", + "main": "dist/index.js", "type": "module", - "bin": { - "mcp-server-webresearch": "dist/index.js" + "bin": "./dist/index.js", + "engines": { + "node": ">=18" }, - "files": [ - "dist" - ], "scripts": { - "build": "tsc && shx chmod +x dist/*.js", - "prepare": "pnpm run build", - "postinstall": "playwright install chromium", - "watch": "tsc --watch", - "dev": "tsx watch index.ts" - }, - "publishConfig": { - "access": "public" + "build": "tsc", + "postbuild": "node -e \"if (process.platform !== 'win32') require('fs').chmodSync('dist/index.js', '755')\"", + "start": "node dist/index.js", + "dev": "ts-node-esm src/index.ts", + "watch": "tsc -w", + "test": "jest", + "lint": "eslint src/**/*.ts", + "clean": "rimraf dist" }, "keywords": [ "mcp", - "model-context-protocol", - "web-research", - "ai", - "web-scraping" + "research", + "web", + "search", + "analysis" + ], + "author": "Kenneth ", + "repository": { + "type": "git", + "url": "https://github.com/mcpnfo/mcp-deepwebresearch.git" + }, + "bugs": { + "url": "https://github.com/mcpnfo/mcp-deepwebresearch/issues" + }, + "homepage": "https://github.com/mcpnfo/mcp-deepwebresearch#readme", + "bin": { + "mcp-deepwebresearch": "./dist/index.js" + }, + "files": [ + "dist", + "README.md", + "LICENSE" ], + "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "1.0.1", - "playwright": "^1.49.0", - "turndown": "^7.1.2" + "@modelcontextprotocol/sdk": "^1.1.1", + "@types/turndown": "^5.0.5", + "cheerio": "^1.0.0", + "html-to-md": "^0.8.6", + "natural": "^8.0.0", + "playwright": "^1.40.0", + "rate-limiter-flexible": "^5.0.0", + "readability": "^0.1.0", + "turndown": "^7.2.0" }, "devDependencies": { - "shx": "^0.3.4", - "tsx": "^4.19.2", - "typescript": "^5.6.2", - "@types/turndown": "^5.0.4" + "@types/cheerio": "^0.22.35", + "@types/jest": "^29.5.0", + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.0.0", + "jest": "^29.0.0", + "rimraf": "^5.0.0", + "ts-jest": "^29.0.0", + "ts-node": "^10.0.0", + "typescript": "^5.0.0" } } \ No newline at end of file diff --git a/parallel-report.txt b/parallel-report.txt new file mode 100644 index 0000000..7be8524 --- /dev/null +++ b/parallel-report.txt @@ -0,0 +1,94 @@ +TURTLE SOUP RESEARCH REPORT + +Historical Evolution: +- Indigenous Origins: Prevalent in tropical coastal regions among indigenous cultures for centuries +- Colonial Spread: Knowledge of turtle soup preparation spread through colonial networks + * Seafaring nations brought the practice from tropical regions to Europe + * Initially considered an aristocratic luxury in Europe + * British Empire played key role in spreading the dish to Asia +- First Royal Taste: British royal family first tried turtle soup in 1728 +- Peak Popularity: Mid-1800s to early 1900s + * Served at prestigious venues from the Ritz to the Titanic + * Commercially manufactured and canned as "Clear Green Turtle Soup" + * Featured at White House events from George Washington to Abraham Lincoln + +Presidential and Royal Connections: +- William Howard Taft: Had a dedicated chef for "Taft Terrapin Soup" (whole turtle with four pounds of veal) + * Insisted on serving it with champagne for important visitors +- Queen Victoria: Initially disliked turtle soup, comparing it to "insects and Tories" + * Later became a fan, with Hatfield House providing £800 worth of turtle for a three-day visit +- Other Presidential Connections: + * George Washington and John Adams served it at the White House + * Abraham Lincoln offered terrapin hors d'oeuvres at his second inauguration + +Cultural Impact and Social Significance: +- Symbol of Status: + * Evolved from aristocratic luxury to middle-class aspiration + * Used to demonstrate wealth and sophistication + * Featured at elaborate "turtle frolics" and society events +- Regional Variations: + * Philadelphia Style: Unique preparation with sherry added just before serving + * New Orleans Style: Thick, buttery, dark brown preparation + * Asian Variations: Often prepared with medicinal herbs + * Singapore: Symbol of prosperity and cultural heritage + +The "Turtle King" Phenomenon: +- Liverpool-based merchant became known as the "Turtle King" +- Specialized in importing live and processed turtles +- Primary supplier to British aristocracy +- Focused mainly on green turtle species + +Culinary Characteristics: +- Preparation Methods: + * Broth becomes extremely gelatinous when cooled + * Turtle meat itself has no characteristic taste + * Flavor depends entirely on seasoning + * Often served with sherry or champagne +- Mock Turtle Soup: + * Created as an alternative for those who couldn't afford real turtle + * Made with calf's head and feet for similar gelatinous texture + * Became popular in its own right + +Historical Medicinal Uses: +- Traditional Beliefs: + * Christopher Columbus (1498) reported use of turtle blood for treating leprosy + * Sailors believed it prevented scurvy (later proved incorrect) + * Various cultures attributed healing properties to turtle soup +- Modern Nutritional Understanding: + * High protein content + * Rich in vitamins A, B1, B2, and B6 + * Contains minerals like phosphorous and zinc + * Approximately 335 calories per 2-cup serving + +Conservation Impact and Modern Status: +- Historical Decimation: + * Caribbean populations severely depleted by 18th century + * Commercial hunting led to near extinction of some species + * Mass production for canning further threatened populations +- Legal Protection: + * 1973 Endangered Species Act prohibited turtle hunting in U.S. waters + * Modern fines up to $20,000 for interfering with sea turtles + * Current fine of $750 for even touching Hawaiian green turtles +- Contemporary Availability: + * Few restaurants still serve authentic turtle soup + * Mostly limited to specific regions (New Orleans, Philadelphia) + * Some Asian countries continue traditional preparation + * Farm-raised turtles now primary source where legal + +Social Clubs and Traditions: +- Hoboken Turtle Club: + * One of America's oldest social clubs + * Motto: "Dum vivimus vivamus" (While we live, let us live) + * Centered around turtle soup consumption +- Philadelphia Legacy: + * Continues through establishments like: + - Sansom Street Oyster House + - The Union League + - Pearl's Oyster Bar in Reading Terminal Market + +Legacy and Modern Perspective: +- Represents significant shift in conservation attitudes +- Symbol of changing cultural values +- Reminder of historical impact on marine species +- Example of how culinary trends can affect wildlife populations +- Demonstrates evolution from luxury item to protected species \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d9f8d5..b3d8050 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,563 +9,4656 @@ importers: .: dependencies: '@modelcontextprotocol/sdk': - specifier: 1.0.1 - version: 1.0.1 + specifier: ^1.1.1 + version: 1.1.1 + '@types/turndown': + specifier: ^5.0.5 + version: 5.0.5 + cheerio: + specifier: ^1.0.0 + version: 1.0.0 + html-to-md: + specifier: ^0.8.6 + version: 0.8.6 + natural: + specifier: ^8.0.0 + version: 8.0.1 playwright: - specifier: ^1.49.0 + specifier: ^1.40.0 version: 1.49.0 + rate-limiter-flexible: + specifier: ^5.0.0 + version: 5.0.4 + readability: + specifier: ^0.1.0 + version: 0.1.0 turndown: - specifier: ^7.1.2 + specifier: ^7.2.0 version: 7.2.0 devDependencies: - '@types/turndown': - specifier: ^5.0.4 - version: 5.0.5 - shx: - specifier: ^0.3.4 - version: 0.3.4 - tsx: - specifier: ^4.19.2 - version: 4.19.2 + '@types/cheerio': + specifier: ^0.22.35 + version: 0.22.35 + '@types/jest': + specifier: ^29.5.0 + version: 29.5.14 + '@types/node': + specifier: ^20.0.0 + version: 20.17.12 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.7.2) + eslint: + specifier: ^8.0.0 + version: 8.57.1 + jest: + specifier: ^29.0.0 + version: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + rimraf: + specifier: ^5.0.0 + version: 5.0.10 + ts-jest: + specifier: ^29.0.0 + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2) + ts-node: + specifier: ^10.0.0 + version: 10.9.2(@types/node@20.17.12)(typescript@5.7.2) typescript: - specifier: ^5.6.2 + specifier: ^5.0.0 version: 5.7.2 packages: - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] + '@asamuzakjp/css-color@2.8.2': + resolution: {integrity: sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==} - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] + '@babel/compat-data@7.26.5': + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} + engines: {node: '>=6.9.0'} - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] + '@babel/generator@7.26.5': + resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} + engines: {node: '>=6.9.0'} - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + '@babel/helper-compilation-targets@7.26.5': + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + engines: {node: '>=6.9.0'} - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] + '@babel/parser@7.26.5': + resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + engines: {node: '>=6.0.0'} + hasBin: true - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.26.5': + resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.5': + resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@csstools/color-helpers@5.0.1': + resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==} engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + '@csstools/css-calc@2.1.1': + resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==} engines: {node: '>=18'} - cpu: [x64] - os: [linux] + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + '@csstools/css-color-parser@3.0.7': + resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==} engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + '@mixmark-io/domino@2.2.0': + resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + '@modelcontextprotocol/sdk@1.1.1': + resolution: {integrity: sha512-siCApQgBn3U8R93TdumLtezRyRIlrA/a63GrTRO1jP31fRyOohpu0iPLvXzsyptxmy7B8GDxr8+r+Phu6mHgzg==} engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] + '@mongodb-js/saslprep@1.1.9': + resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==} - '@mixmark-io/domino@2.2.0': - resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} - '@modelcontextprotocol/sdk@1.0.1': - resolution: {integrity: sha512-slLdFaxQJ9AlRg+hw28iiTtGvShAOgOKXcD0F91nUcRYiOMuS9ZBYjcdNZRXW9G5JQ511GRTdUy1zQVZDpJ+4w==} + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} - '@types/turndown@5.0.5': - resolution: {integrity: sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==} + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} + '@redis/client@1.6.0': + resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==} + engines: {node: '>=14'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} + '@redis/json@1.0.7': + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} + '@redis/search@1.2.0': + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true + '@redis/time-series@1.1.0': + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} + '@types/cheerio@0.22.35': + resolution: {integrity: sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - playwright-core@1.49.0: - resolution: {integrity: sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==} - engines: {node: '>=18'} - hasBin: true + '@types/node@20.17.12': + resolution: {integrity: sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==} - playwright@1.49.0: - resolution: {integrity: sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==} - engines: {node: '>=18'} - hasBin: true + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - raw-body@3.0.0: - resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} - engines: {node: '>= 0.8'} + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} + '@types/turndown@5.0.5': + resolution: {integrity: sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true + '@typescript-eslint/eslint-plugin@6.21.0': + resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@6.21.0': + resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@6.21.0': + resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/type-utils@6.21.0': + resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@6.21.0': + resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/typescript-estree@6.21.0': + resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true - shx@0.3.4: - resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} - engines: {node: '>=6'} - hasBin: true + '@typescript-eslint/utils@6.21.0': + resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} + '@typescript-eslint/visitor-keys@6.21.0': + resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} + engines: {node: ^16.0.0 || >=18.0.0} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + '@ungap/structured-clone@1.2.1': + resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - tsx@4.19.2: - resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} - engines: {node: '>=18.0.0'} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} hasBin: true - turndown@7.2.0: - resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + afinn-165-financialmarketnews@3.0.0: + resolution: {integrity: sha512-0g9A1S3ZomFIGDTzZ0t6xmv4AuokBvBmpes8htiyHpH7N4xDmvSQL6UxL/Zcs2ypRb3VwgCscaD8Q3zEawKYhw==} - typescript@5.7.2: - resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} - engines: {node: '>=14.17'} - hasBin: true + afinn-165@1.0.4: + resolution: {integrity: sha512-7+Wlx3BImrK0HiG6y3lU4xX7SpBPSSu8T9iguPMlaueRFxjbYwAQrp9lqZUuFikqKbd/en8lVREILvP2J80uJA==} - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} -snapshots: + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} - '@esbuild/aix-ppc64@0.23.1': - optional: true + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} - '@esbuild/android-arm64@0.23.1': - optional: true + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} - '@esbuild/android-arm@0.23.1': - optional: true + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} - '@esbuild/android-x64@0.23.1': - optional: true + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} - '@esbuild/darwin-arm64@0.23.1': - optional: true + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} - '@esbuild/darwin-x64@0.23.1': - optional: true + apparatus@0.0.10: + resolution: {integrity: sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==} + engines: {node: '>=0.2.6'} - '@esbuild/freebsd-arm64@0.23.1': - optional: true + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - '@esbuild/freebsd-x64@0.23.1': - optional: true + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - '@esbuild/linux-arm64@0.23.1': - optional: true + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - '@esbuild/linux-arm@0.23.1': - optional: true + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} - '@esbuild/linux-ia32@0.23.1': - optional: true + async@2.6.4: + resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==} - '@esbuild/linux-loong64@0.23.1': - optional: true + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - '@esbuild/linux-mips64el@0.23.1': - optional: true + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - '@esbuild/linux-ppc64@0.23.1': - optional: true + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 - '@esbuild/linux-riscv64@0.23.1': - optional: true + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} - '@esbuild/linux-s390x@0.23.1': - optional: true + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@esbuild/linux-x64@0.23.1': - optional: true + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} + peerDependencies: + '@babel/core': ^7.0.0 - '@esbuild/netbsd-x64@0.23.1': - optional: true + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 - '@esbuild/openbsd-arm64@0.23.1': - optional: true + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - '@esbuild/openbsd-x64@0.23.1': - optional: true + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} - '@esbuild/sunos-x64@0.23.1': - optional: true + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - '@esbuild/win32-arm64@0.23.1': - optional: true + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - '@esbuild/win32-ia32@0.23.1': - optional: true + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - '@esbuild/win32-x64@0.23.1': - optional: true + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} - '@mixmark-io/domino@2.2.0': {} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true - '@modelcontextprotocol/sdk@1.0.1': - dependencies: - content-type: 1.0.5 - raw-body: 3.0.0 - zod: 3.23.8 + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} - '@types/turndown@5.0.5': {} + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - balanced-match@1.0.2: {} + bson@6.10.1: + resolution: {integrity: sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==} + engines: {node: '>=16.20.1'} - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - bytes@3.1.2: {} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} - concat-map@0.0.1: {} + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} - content-type@1.0.5: {} + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + engines: {node: '>= 0.4'} - depd@2.0.0: {} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} - fs.realpath@1.0.0: {} + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} - fsevents@2.3.2: - optional: true + caniuse-lite@1.0.30001692: + resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} - fsevents@2.3.3: - optional: true + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} - function-bind@1.1.2: {} + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 + cjs-module-lexer@1.4.1: + resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} - inherits@2.0.4: {} + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - interpret@1.4.0: {} + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - minimist@1.2.8: {} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} - once@1.4.0: - dependencies: - wrappy: 1.0.2 + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - path-is-absolute@1.0.1: {} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} - path-parse@1.0.7: {} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - playwright-core@1.49.0: {} + corser@2.0.1: + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + engines: {node: '>= 0.4.0'} - playwright@1.49.0: - dependencies: - playwright-core: 1.49.0 - optionalDependencies: - fsevents: 2.3.2 + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true - raw-body@3.0.0: + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssstyle@4.2.1: + resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==} + engines: {node: '>=18'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.80: + resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.18.0: + resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.2.7: + resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-to-md@0.8.6: + resolution: {integrity: sha512-KaRoYxML9AIETfgz5tgI1ioCsT8+skHTIILzrDV66XwCxuWUVjwCcpbnfeUwT9r8VBrfDAa8B5p5b5Pcs9LtRg==} + + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + + htmlparser@1.7.7: + resolution: {integrity: sha512-zpK66ifkT0fauyFh2Mulrq4AqGTucxGtOhZ8OjkbSfcCpkqQEI8qRkY0tSQSJNAQ4HUZkgWaU4fK4EH6SVH9PQ==} + engines: {node: '>=0.1.33'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-server@14.1.1: + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + engines: {node: '>=12'} + hasBin: true + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@26.0.0: + resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kareem@2.6.3: + resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==} + engines: {node: '>=12.0.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + memjs@1.3.2: + resolution: {integrity: sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==} + engines: {node: '>=0.10.0'} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mjsunit.runner@0.1.3: + resolution: {integrity: sha512-m0LYKN/kv+Yx1TwYGneCsztgPp5zTa5Erx0KuF20opXjeEO+5xOCjwoaq+qx69/tte3DaFD6atgcf/JFW0l8Pw==} + engines: {node: '>=0.1.9'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.12.0: + resolution: {integrity: sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 || ^2.0.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + mongoose@8.9.4: + resolution: {integrity: sha512-DndoI01aV/q40P9DiYDXsYjhj8vZjmmuFwcC3Tro5wFznoE1z6Fe2JgMnbLR6ghglym5ziYizSfAJykp+UPZWg==} + engines: {node: '>=16.20.1'} + + mpath@0.9.0: + resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==} + engines: {node: '>=4.0.0'} + + mquery@5.0.0: + resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} + engines: {node: '>=14.0.0'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + natural@8.0.1: + resolution: {integrity: sha512-VVw8O5KrfvwqAFeNZEgBbdgA+AQaBlHcXEootWU7TWDaFWFI0VLfzyKMsRjnfdS3cVCpWmI04xLJonCvEv11VQ==} + engines: {node: '>=0.4.10'} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nwsapi@2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.13.1: + resolution: {integrity: sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + playwright-core@1.49.0: + resolution: {integrity: sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.49.0: + resolution: {integrity: sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==} + engines: {node: '>=18'} + hasBin: true + + portfinder@1.0.32: + resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==} + engines: {node: '>= 0.12.0'} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + qs@6.13.1: + resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + rate-limiter-flexible@5.0.4: + resolution: {integrity: sha512-ftYHrIfSqWYDIJZ4yPTrgOduByAp+86gUS9iklv0JoXVM8eQCAjTnydCj1hAT4MmhmkSw86NaFEJ28m/LC1pKA==} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readability@0.1.0: + resolution: {integrity: sha512-rt7d3Us0ev/VNBfsSdNHjWn1xibcfkvpQ/Ahtt87s9nzcIri5L4b6r3oxqBjKh1BhTwJcGk6kUd0Y6Q5jl+20Q==} + engines: {node: '>=0.3.1'} + + redis@4.7.0: + resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + secure-compare@3.0.1: + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + sift@17.1.3: + resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + stopwords-iso@1.1.0: + resolution: {integrity: sha512-I6GPS/E0zyieHehMRPQcqkiBMJKGgLta+1hREixhoLPqEA0AlVFiC43dl8uPpmkkeRdDMzYRWFWk5/l9x7nmNg==} + engines: {node: '>=0.10.0'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + sylvester@0.0.12: + resolution: {integrity: sha512-SzRP5LQ6Ts2G5NyAa/jg16s8e3R7rfdFjizy1zeoecYWw+nGL+YA1xZvW/+iJmidBGSdLkuvdwTYEyJEb+EiUw==} + engines: {node: '>=0.2.6'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + tldts-core@6.1.71: + resolution: {integrity: sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==} + + tldts@6.1.71: + resolution: {integrity: sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==} + hasBin: true + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tough-cookie@5.1.0: + resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==} + engines: {node: '>=16'} + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-jest@29.2.5: + resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + turndown@7.2.0: + resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + engines: {node: '>=14.17'} + hasBin: true + + underscore@1.13.7: + resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici@6.21.0: + resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} + engines: {node: '>=18.17'} + + union@0.5.0: + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + engines: {node: '>= 0.8.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} + engines: {node: '>=18'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordnet-db@3.1.14: + resolution: {integrity: sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==} + engines: {node: '>=0.6.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@asamuzakjp/css-color@2.8.2': + dependencies: + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + lru-cache: 11.0.2 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.5': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.5': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.26.5': + dependencies: + '@babel/compat-data': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.5 + '@babel/types': 7.26.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.26.5': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + + '@babel/parser@7.26.5': + dependencies: + '@babel/types': 7.26.5 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + + '@babel/traverse@7.26.5': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.5 + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.5': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@bcoe/v8-coverage@0.2.3': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@csstools/color-helpers@5.0.1': {} + + '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/color-helpers': 5.0.1 + '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + + '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.17.12 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 20.17.12 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.26.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.17.12 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@mixmark-io/domino@2.2.0': {} + + '@modelcontextprotocol/sdk@1.1.1': + dependencies: + content-type: 1.0.5 + raw-body: 3.0.0 + zod: 3.23.8 + + '@mongodb-js/saslprep@1.1.9': + dependencies: + sparse-bitfield: 3.0.3 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.18.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@redis/bloom@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/client@1.6.0': + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + + '@redis/graph@1.1.1(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/json@1.0.7(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/search@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/time-series@1.1.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@sinclair/typebox@0.27.8': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.26.5 + '@babel/types': 7.26.5 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.5 + + '@types/cheerio@0.22.35': + dependencies: + '@types/node': 20.17.12 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 20.17.12 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/json-schema@7.0.15': {} + + '@types/node@20.17.12': + dependencies: + undici-types: 6.19.8 + + '@types/semver@7.5.8': {} + + '@types/stack-utils@2.0.3': {} + + '@types/turndown@5.0.5': {} + + '@types/webidl-conversions@7.0.3': {} + + '@types/whatwg-url@11.0.5': + dependencies: + '@types/webidl-conversions': 7.0.3 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.7.2) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.1)(typescript@5.7.2) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.0 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.6.3 + ts-api-utils: 1.4.3(typescript@5.7.2) + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.7.2)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.0 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + + '@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.7.2)': + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.7.2) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.1)(typescript@5.7.2) + debug: 4.4.0 + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.7.2) + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.21.0': {} + + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.7.2)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.4.0 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.3 + ts-api-utils: 1.4.3(typescript@5.7.2) + optionalDependencies: + typescript: 5.7.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.7.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.7.2) + eslint: 8.57.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@6.21.0': + dependencies: + '@typescript-eslint/types': 6.21.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.1': {} + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + afinn-165-financialmarketnews@3.0.0: {} + + afinn-165@1.0.4: {} + + agent-base@7.1.3: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + apparatus@0.0.10: + dependencies: + sylvester: 0.0.12 + + arg@4.1.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + async@2.6.4: + dependencies: + lodash: 4.17.21 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + babel-jest@29.7.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.26.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.5 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) + + babel-preset-jest@29.6.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + + balanced-match@1.0.2: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001692 + electron-to-chromium: 1.5.80 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + bson@6.10.1: {} + + buffer-from@1.1.2: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.1: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.7 + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001692: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.21.0 + whatwg-mimetype: 4.0.0 + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.1: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cluster-key-slot@1.1.2: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + corser@2.0.1: {} + + create-jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-require@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.1.0: {} + + cssstyle@4.2.1: + dependencies: + '@asamuzakjp/css-color': 2.8.2 + rrweb-cssom: 0.8.0 + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decimal.js@10.4.3: {} + + dedent@1.5.3: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + detect-newline@3.1.0: {} + + diff-sequences@29.6.3: {} + + diff@4.0.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv@16.4.7: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-to-chromium@1.5.80: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encoding-sniffer@0.2.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + entities@4.5.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.1 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventemitter3@4.0.7: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.18.0: + dependencies: + reusify: 1.0.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.2: {} + + follow-redirects@1.15.9: {} + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + generic-pool@3.9.0: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.2.7: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + 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 + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.0.0 + + get-stream@6.0.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-escaper@2.0.2: {} + + html-to-md@0.8.6: {} + + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + htmlparser@1.7.7: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.9 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-server@14.1.1: + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.32 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-arrayish@0.2.1: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-potential-custom-element-name@1.0.1: {} + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.17.12 + ts-node: 10.9.2(@types/node@20.17.12)(typescript@5.7.2) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.17.12 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.26.2 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + chalk: 4.1.2 + cjs-module-lexer: 1.4.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.5 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.5 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.17.12 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 20.17.12 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@26.0.0: + dependencies: + cssstyle: 4.2.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.1 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.16 + parse5: 7.2.1 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.1.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + kareem@2.6.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash@4.17.21: {} + + lru-cache@10.4.3: {} + + lru-cache@11.0.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@4.0.0: + dependencies: + semver: 7.6.3 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + math-intrinsics@1.1.0: {} + + memjs@1.3.2: {} + + memory-pager@1.5.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + mjsunit.runner@0.1.3: {} + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mongodb-connection-string-url@3.0.1: + dependencies: + '@types/whatwg-url': 11.0.5 + whatwg-url: 13.0.0 + + mongodb@6.12.0: + dependencies: + '@mongodb-js/saslprep': 1.1.9 + bson: 6.10.1 + mongodb-connection-string-url: 3.0.1 + + mongoose@8.9.4: + dependencies: + bson: 6.10.1 + kareem: 2.6.3 + mongodb: 6.12.0 + mpath: 0.9.0 + mquery: 5.0.0 + ms: 2.1.3 + sift: 17.1.3 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + + mpath@0.9.0: {} + + mquery@5.0.0: + dependencies: + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + natural@8.0.1: + dependencies: + afinn-165: 1.0.4 + afinn-165-financialmarketnews: 3.0.0 + apparatus: 0.0.10 + dotenv: 16.4.7 + http-server: 14.1.1 + memjs: 1.3.2 + mongoose: 8.9.4 + pg: 8.13.1 + redis: 4.7.0 + safe-stable-stringify: 2.5.0 + stopwords-iso: 1.1.0 + sylvester: 0.0.12 + underscore: 1.13.7 + uuid: 9.0.1 + wordnet-db: 3.1.14 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - debug + - gcp-metadata + - kerberos + - mongodb-client-encryption + - pg-native + - snappy + - socks + - supports-color + + node-int64@0.4.0: {} + + node-releases@2.0.19: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nwsapi@2.2.16: {} + + object-inspect@1.13.3: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + opener@1.5.2: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.2.1 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.2.1 + + parse5@7.2.1: + dependencies: + entities: 4.5.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.7.0(pg@8.13.1): + dependencies: + pg: 8.13.1 + + pg-protocol@1.7.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.13.1: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.1) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + pirates@4.0.6: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + playwright-core@1.49.0: {} + + playwright@1.49.0: + dependencies: + playwright-core: 1.49.0 + optionalDependencies: + fsevents: 2.3.2 + + portfinder@1.0.32: + dependencies: + async: 2.6.4 + debug: 3.2.7 + mkdirp: 0.5.6 + transitivePeerDependencies: + - supports-color + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + prelude-ls@1.2.1: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + qs@6.13.1: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + rate-limiter-flexible@5.0.4: {} + + raw-body@3.0.0: dependencies: bytes: 3.1.2 http-errors: 2.0.0 iconv-lite: 0.6.3 unpipe: 1.0.0 - rechoir@0.6.2: + react-is@18.3.1: {} + + readability@0.1.0: dependencies: - resolve: 1.22.8 + htmlparser: 1.7.7 + jsdom: 26.0.0 + mjsunit.runner: 0.1.3 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + redis@4.7.0: + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.0) + '@redis/client': 1.6.0 + '@redis/graph': 1.1.1(@redis/client@1.6.0) + '@redis/json': 1.0.7(@redis/client@1.6.0) + '@redis/search': 1.2.0(@redis/client@1.6.0) + '@redis/time-series': 1.1.0(@redis/client@1.6.0) - resolve-pkg-maps@1.0.0: {} + require-directory@2.1.1: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve.exports@2.0.3: {} resolve@1.22.8: dependencies: @@ -573,42 +4666,362 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + + rrweb-cssom@0.8.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + secure-compare@3.0.1: {} + + semver@6.3.1: {} + + semver@7.6.3: {} + setprototypeof@1.2.0: {} - shelljs@0.8.5: + shebang-command@2.0.0: dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} - shx@0.3.4: + side-channel-list@1.0.0: dependencies: - minimist: 1.2.8 - shelljs: 0.8.5 + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + 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 + + sift@17.1.3: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + sparse-bitfield@3.0.3: + dependencies: + memory-pager: 1.5.0 + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 statuses@2.0.1: {} + stopwords-iso@1.1.0: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + sylvester@0.0.12: {} + + symbol-tree@3.2.4: {} + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + text-table@0.2.0: {} + + tldts-core@6.1.71: {} + + tldts@6.1.71: + dependencies: + tldts-core: 6.1.71 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + toidentifier@1.0.1: {} - tsx@4.19.2: + tough-cookie@5.1.0: + dependencies: + tldts: 6.1.71 + + tr46@4.1.1: + dependencies: + punycode: 2.3.1 + + tr46@5.0.0: dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 + punycode: 2.3.1 + + ts-api-utils@1.4.3(typescript@5.7.2): + dependencies: + typescript: 5.7.2 + + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)))(typescript@5.7.2): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.17.12)(ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.7.2 + yargs-parser: 21.1.1 optionalDependencies: - fsevents: 2.3.3 + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + + ts-node@10.9.2(@types/node@20.17.12)(typescript@5.7.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.17.12 + acorn: 8.14.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.7.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 turndown@7.2.0: dependencies: '@mixmark-io/domino': 2.2.0 + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + typescript@5.7.2: {} + underscore@1.13.7: {} + + undici-types@6.19.8: {} + + undici@6.21.0: {} + + union@0.5.0: + dependencies: + qs: 6.13.1 + unpipe@1.0.0: {} + update-browserslist-db@1.1.2(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@4.0.1: {} + + uuid@9.0.1: {} + + v8-compile-cache-lib@3.0.1: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@13.0.0: + dependencies: + tr46: 4.1.1 + webidl-conversions: 7.0.0 + + whatwg-url@14.1.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wordnet-db@3.1.14: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + ws@8.18.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + zod@3.23.8: {} diff --git a/prompt_generator.py b/prompt_generator.py new file mode 100644 index 0000000..b181529 --- /dev/null +++ b/prompt_generator.py @@ -0,0 +1,245 @@ +import json +import boto3 +import os +from typing import Dict, List +import subprocess +import json +from datetime import datetime, timezone + +class PromptGenerator: + def __init__(self): + self.bedrock = boto3.client( + service_name='bedrock-runtime', + region_name=os.getenv('AWS_REGION'), + aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), + aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY') + ) + self.model_id = "anthropic.claude-3-5-sonnet-20241022-v2:0" + + def _gather_topic_info(self, topic: str) -> str: + """Gather initial information about the topic using the research system.""" + try: + print("\n[INFO] Starting topic information gathering process...") + print(f"[INFO] Topic: {topic}") + + # Ensure TypeScript is compiled + if not os.path.exists('dist/quick_research.js'): + print("[BUILD] TypeScript code not found, attempting compilation...") + try: + subprocess.run(['npm', 'run', 'build'], check=True) + print("[BUILD] Successfully compiled TypeScript code") + except subprocess.CalledProcessError as e: + print(f"[BUILD] Failed to compile TypeScript: {str(e)}") + return "" + else: + print("[BUILD] Found compiled TypeScript code") + + # Run quick research + print("\n[RESEARCH] Initiating quick research process...") + print(f"[CMD] Executing: node dist/quick_research.js {topic}") + + process = subprocess.Popen( + ['node', 'dist/quick_research.js', topic], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding='utf-8' + ) + + # Wait for quick research to complete (max 600 seconds / 10 minutes) + try: + print("[WAIT] Waiting for research process (timeout: 600s)...") + stdout, stderr = process.communicate(timeout=600) + + print(f"[DEBUG] Process return code: {process.returncode}") + if stderr: + print(f"[DEBUG] Process stderr: {stderr}") + if stdout: + print(f"[DEBUG] Process stdout length: {len(stdout)} characters") + + if process.returncode != 0: + print(f"[ERROR] Quick research failed: {stderr}") + print("[INFO] Falling back to direct Claude prompt generation") + return "" + + # Extract research results from stdout + if "=== Quick Research Results ===" in stdout: + context = stdout.split("=== Quick Research Results ===")[1].strip() + if context: + print("[INFO] Successfully gathered topic context") + print(f"[DEBUG] Context length: {len(context)} characters") + return context + + print("[WARN] No research results found in process output") + print("[INFO] Falling back to direct Claude prompt generation") + return "" + + except subprocess.TimeoutExpired: + process.kill() + print("[WARN] Quick research timed out after 600 seconds (10 minutes)") + print("[DEBUG] Attempting to collect any partial output...") + try: + stdout, stderr = process.communicate() + if stdout: + print(f"[DEBUG] Partial stdout length: {len(stdout)} characters") + if stderr: + print(f"[DEBUG] Error output: {stderr}") + except: + print("[DEBUG] Failed to collect partial output") + print("[INFO] Falling back to direct Claude prompt generation") + return "" + + except Exception as e: + print(f"[ERROR] Unexpected error during topic info gathering: {str(e)}") + print("[DEBUG] Error type:", type(e).__name__) + print("[INFO] Falling back to direct Claude prompt generation") + return "" + + def generate_improved_prompt(self, topic: str) -> str: + """Generate an improved research prompt for the given topic.""" + print("\n[STEP] Starting improved prompt generation process...") + print(f"[INFO] Generating prompt for topic: {topic}") + + # First gather information about the topic + print("[STEP] Attempting to gather topic context...") + topic_info = self._gather_topic_info(topic) + + if topic_info: + print("[INFO] Successfully gathered topic context") + print(f"[DEBUG] Context length: {len(topic_info)} characters") + else: + print("[INFO] No context gathered, using base template") + + print("[STEP] Creating prompt with Claude 3.5...") + + # Create a prompt that incorporates the gathered information + prompt = ( + f"You are an expert research prompt engineer. Your task is to create a comprehensive " + f"research prompt for: \"{topic}\"\n\n" + f"Context about the topic:\n{topic_info if topic_info else 'No additional context available.'}\n\n" + f"Based on the above context about what this topic is, create a tailored research plan that:\n\n" + f"1. Focuses on the most relevant types of sources for this specific content\n" + f"2. Includes appropriate metrics and analysis methods for this type of work\n" + f"3. Considers the historical and current context\n" + f"4. Examines the key themes and claims made\n" + f"5. Evaluates the impact and implications\n" + f"6. Addresses any relevant criticisms or controversies\n\n" + f"Format your response as a clear research plan with:\n" + f"- Numbered main sections that match the content type\n" + f"- Specific, actionable research tasks\n" + f"- Appropriate depth and scope for this topic\n" + f"- Clear connection to the topic's context and purpose\n\n" + f"Begin your response with \"Research and analyze {topic} by:\" followed by your tailored research plan.\n" + f"Make sure the plan reflects understanding of what this topic actually is." + ) + + try: + print("[API] Preparing Bedrock API request...") + request_body = { + "messages": [ + { + "role": "user", + "content": prompt + } + ], + "max_tokens": 4000, + "temperature": 0.7, + "top_p": 0.999, + "anthropic_version": "bedrock-2023-05-31" + } + print(f"[DEBUG] Request max_tokens: {request_body['max_tokens']}") + print(f"[DEBUG] Request temperature: {request_body['temperature']}") + + print("[API] Sending request to Bedrock Claude 3.5...") + response = self.bedrock.invoke_model( + modelId=self.model_id, + body=json.dumps(request_body) + ) + + print("[API] Processing Bedrock response...") + response_body = json.loads(response.get('body').read()) + + if 'content' not in response_body or not response_body['content']: + print("[ERROR] Invalid response structure from Bedrock") + print("[DEBUG] Response body:", response_body) + print("[INFO] Falling back to default prompt template") + return self._get_fallback_prompt(topic) + + improved_prompt = response_body['content'][0].get('text', '') + print(f"[DEBUG] Generated prompt length: {len(improved_prompt)} characters") + + print("[STEP] Formatting final prompt...") + formatted_prompt = self._format_prompt(improved_prompt) + print("[SUCCESS] Successfully generated improved prompt") + return formatted_prompt + + except Exception as e: + print(f"[ERROR] Failed to generate improved prompt: {str(e)}") + print("[DEBUG] Error type:", type(e).__name__) + print("[INFO] Falling back to default prompt template") + return self._get_fallback_prompt(topic) + + def _format_prompt(self, prompt: str) -> str: + """Format the generated prompt with consistent styling.""" + sections = prompt.split('\n') + formatted_sections = [] + + for section in sections: + # Clean up section formatting + section = section.strip() + if section: + # Add proper indentation for subsections + if section.startswith('- '): + section = ' ' + section + formatted_sections.append(section) + + # Join with double newlines for better readability + formatted_prompt = '\n'.join(formatted_sections) + + # Wrap in XML-style tags for clear identification + return f"\n{formatted_prompt}\n" + + def _get_fallback_prompt(self, topic: str) -> str: + """Generate a fallback prompt if the API call fails.""" + return f""" +Research and analyze {topic} by: + +(1) Find peer-reviewed publications and technical papers from leading institutions and organizations in this field. + +(2) Find official documentation, white papers, and public statements from major companies and organizations. + +(3) Find and analyze concrete metrics and benchmarks, including: + - Performance measurements + - Implementation success rates + - Adoption patterns + - Technical limitations + +(4) Find information about current and emerging technologies, including: + - Technical specifications + - Implementation methodologies + - Integration approaches + - Best practices + +(5) Find expert analysis and academic research on: + - Current state of the field + - Future projections + - Potential impacts + - Development patterns + +(6) Find documentation of: + - Safety measures + - Best practices + - Regulatory frameworks + - Risk assessments + +This structured approach ensures comprehensive coverage while maintaining focus on credible sources and concrete developments. +""" + +def main(): + """Test the prompt generator.""" + generator = PromptGenerator() + test_topic = "AI Safety and Alignment Methods 2025" + improved_prompt = generator.generate_improved_prompt(test_topic) + print(improved_prompt) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/quantum-deep-research-report.txt b/quantum-deep-research-report.txt new file mode 100644 index 0000000..d9899f4 --- /dev/null +++ b/quantum-deep-research-report.txt @@ -0,0 +1,15 @@ +Deep Research Report on Quantum Computing Advancements + +Sources: +1. [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/) +2. [2025 Will See Huge Advances in Quantum Computing. So What is a Quantum Chip And How Does it Work?](https://thequantuminsider.com/2025/01/08/2025-will-see-huge-advances-in-quantum-computing-so-what-is-a-quantum-chip-and-how-does-it-work/) +3. [5 breakthroughs made possible by quantum technologies](https://www.polytechnique-insights.com/en/columns/science/5-breakthroughs-made-possible-by-quantum-technologies/) +4. [Quantum Computing: Developments in the UK and US | Inside Privacy](https://www.insideprivacy.com/data-privacy/quantum-computing-developments-in-the-uk-and-us/) +5. [Exploring the Latest Quantum Computing Advancements in 2024 - FirstIgnite](https://firstignite.com/exploring-the-latest-quantum-computing-advancements-in-2024/) +6. [World Quantum Day 2024: The latest developments in quantum science and technology | Pritzker School of Molecular Engineering | The University of Chicago](https://pme.uchicago.edu/news/world-quantum-day-2024-latest-developments-quantum-science-and-technology) +7. [Quantum Computing: Potential and Challenges ahead - Plain Concepts](https://www.plainconcepts.com/quantum-computing-potential-challenges/) +8. [Quantum Technology: Applications and Implications](https://www.csis.org/analysis/quantum-technology-applications-and-implications) +9. [Quantum computing technology pushes for IT advantage | TechTarget](https://www.techtarget.com/searchcio/feature/Quantum-computing-technology-pushes-for-IT-advantage) +10. [References](https://www.wevolver.com/article/breakthroughs-in-quantum-computing) +11. [What's next for quantum computing | MIT Technology Review](https://www.technologyreview.com/2023/01/06/1066317/whats-next-for-quantum-computing/) +12. [What is quantum computing?](https://www.ibm.com/think/topics/quantum-computing) \ No newline at end of file diff --git a/quantum-parallel-report.txt b/quantum-parallel-report.txt new file mode 100644 index 0000000..22360c7 --- /dev/null +++ b/quantum-parallel-report.txt @@ -0,0 +1,93 @@ +Parallel Search Report on Quantum Computing Advancements + +1. **Quantum Computing advancements** + - [Quantum computing technology pushes for IT advantage](https://www.techtarget.com/searchcio/feature/Quantum-computing-technology-pushes-for-IT-advantage): Nov 27, 2024 — Quantum computing technology, evolving in GenAI's shadow, looks for advances to help it gain 'quantum advantage.' Read about trends in this ... + - [Quantum Computing Is Coming Faster Than You Think](https://www.forbes.com/sites/tiriasresearch/2023/11/28/quantum-computing-is-coming-faster-than-you-think/): Nov 28, 2023 — Another reason is the continued advancements being made in quantum computing is improvements in quantum chips, control logic, systems, and ... + - [Breakthroughs in Quantum Computing](https://www.wevolver.com/article/breakthroughs-in-quantum-computing): Aug 19, 2024 — Another groundbreaking advancement is the teleportation of quantum information over distances exceeding 1,200km, facilitated by the Micius ... + - [What's next for quantum computing](https://www.technologyreview.com/2023/01/06/1066317/whats-next-for-quantum-computing/): Jan 6, 2023 — In 2023, progress in quantum computing will be defined less by big hardware announcements than by researchers consolidating years of hard work. + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): Jan. 9, 2025 — Researchers have recently achieved a significant breakthrough in the development of next-generation carbon-based quantum materials, opening new ... + - [The latest developments in quantum science and technology ...](https://pme.uchicago.edu/news/world-quantum-day-2024-latest-developments-quantum-science-and-technology): Apr 12, 2024 — Many more advancements in quantum technology are yet to come. Secure communication through metropolitan-scale entangled quantum networks, ... + - [2025 Will See Huge Advances in Quantum Computing. So ...](https://thequantuminsider.com/2025/01/08/2025-will-see-huge-advances-in-quantum-computing-so-what-is-a-quantum-chip-and-how-does-it-work/): 7 days ago — Many experts are expecting big advance in quantum computing in 2025, but what is a quantum chip and how does it work? + - [Quantum Computing in 2024: Breakthroughs, Challenges ...](https://microtime.com/quantum-computing-in-2024-breakthroughs-challenges-and-what-lies-ahead/): Sep 5, 2024 — 2024 has been a year of significant progress in the field of QC, with several key breakthroughs that are bringing us closer to realizing its potential. + - [Advancements in Quantum Computing—Viewpoint](https://link.springer.com/article/10.1007/s13222-024-00467-4): by SML Pfaendler · 2024 · Cited by 10 — This article introduces key technologies and discussion points revolving around the evaluation of quantum computing technology readiness and adoption. + +2. **Latest in Quantum Computing** + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): Quantum Computer Research. Read the latest news in developing quantum computers. + - [The Quantum Insider: Quantum Computing News & Top Stories](https://thequantuminsider.com/): Find the latest Quantum Computing news, data, market research, and insights. To stay up to date with the quantum market click here! + - [Quantum computing](https://news.mit.edu/topic/quantum-computing): Quantum computing ; Physicists measure quantum geometry for the first time · January 13, 2025 ; MIT physicists predict exotic form of matter with potential for ... + - [Quantum Computing - latest research news and features](https://phys.org/tags/quantum+computing/): All the latest science news on quantum computing from Phys.org. Find the latest news, advancements, and breakthroughs. + - [The latest developments in quantum science and technology ...](https://pme.uchicago.edu/news/world-quantum-day-2024-latest-developments-quantum-science-and-technology): Apr 12, 2024 — Many more advancements in quantum technology are yet to come. Secure communication through metropolitan-scale entangled quantum networks, ... + - [Meet Willow, our state-of-the-art quantum chip](https://blog.google/technology/research/google-willow-quantum-chip/): Dec 9, 2024 — Google has developed a new quantum chip called Willow, which significantly reduces errors as it scales up, a major breakthrough in quantum error correction. + - [Quantum Computing News, Quantum Articles, Quantum Industry](https://quantumzeitgeist.com/): Quantum Computing News and Quantum News. Technology News from around the planet. Exciting Latest Developments in Quantum Tech. + +3. **Quantum Computing technology news** + - [The Quantum Insider: Quantum Computing News & Top Stories](https://thequantuminsider.com/): Find the latest Quantum Computing news, data, market research, and insights. To stay up to date with the quantum market click here! + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): The technology could transform computing, telecommunications, and ... Novel Graphene Ribbons Poised to Advance Quantum Technologies. Jan. 9, 2025 — Researchers ... + - [Quantum computing](https://news.mit.edu/topic/quantum-computing): Quantum computing. Download RSS feed: News Articles / In the Media / Audio. Displaying 1 - 15 of 182 news articles related to this topic. Show: News Articles. + - [Quantum Computing - latest research news and features](https://phys.org/tags/quantum+computing/): All the latest science news on quantum computing from Phys.org. Find the latest news, advancements, and breakthroughs. + - [Quantum computing - BBC News](https://www.bbc.com/news/topics/cyz9ex69xwlt): From unhackable communication networks to powerful computers, quantum technology promises huge advances. + - [Quantum Computing | Latest News, Photos & Videos](https://www.wired.com/tag/quantum-computing/): Find the latest Quantum Computing news from WIRED. See related science and technology articles, photos, slideshows and videos. + - [Quantum Computing News, Quantum Articles, Quantum Industry](https://quantumzeitgeist.com/): Quantum Computing News and Quantum News. Technology News from around the planet. Exciting Latest Developments in Quantum Tech. + +4. **Quantum Computing breakthroughs** + - [Breakthroughs in Quantum Computing](https://www.wevolver.com/article/breakthroughs-in-quantum-computing): Aug 19, 2024 — 2023 was a landmark year for quantum computing, with innovative breakthroughs promising to reshape our technological landscape and revolutionize how we solve ... + - [Quantum Computing in 2024: Breakthroughs, Challenges ...](https://microtime.com/quantum-computing-in-2024-breakthroughs-challenges-and-what-lies-ahead/): Sep 5, 2024 — 2024 has been a year of significant progress in the field of QC, with several key breakthroughs that are bringing us closer to realizing its potential. + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): 9, 2025 — Researchers have recently achieved a significant breakthrough in the development of next-generation carbon-based quantum materials, opening new ... + - [Professor Achieves Major Quantum Computing Breakthrough](https://news.northeastern.edu/2024/07/12/quantum-computing-breakthrough-manufacturing/): Jul 12, 2024 — Northeastern professor achieves major breakthrough in the manufacture of quantum computing components. Assistant professor Yoseob Yoon has ... + - ['A truly remarkable breakthrough': Google's new quantum ...](https://www.nature.com/articles/d41586-024-04028-3): Dec 9, 2024 — Researchers at Google have built a chip that has enabled them to demonstrate the first 'below threshold' quantum calculations. + - [How Quantum AI Will Reshape Our World](https://www.forbes.com/sites/bernardmarr/2024/10/08/the-next-breakthrough-in-artificial-intelligence-how-quantum-ai-will-reshape-our-world/): Oct 8, 2024 — Quantum AI, the fusion of quantum computing and artificial intelligence, is poised to revolutionize industries from finance to healthcare. + - [Quantum computing takes a giant leap with breakthrough ...](https://www.earth.com/news/quantum-computing-giant-leap-forward-breakthrough-ultra-pure-silicon-discovery/): May 12, 2024 — Scientists have produced an enhanced, ultra-pure form of silicon that is crucial for paving the way towards scalable quantum computing. + - [DARPA-Funded Research Leads to Quantum Computing ...](https://www.darpa.mil/news/2023/quantum-computing-breakthrough): Dec 6, 2023 — DARPA-funded research leads to quantum computing breakthrough. Harvard-led team develops novel logical qubits to enable scalable quantum computers. + - [Google Makes a Major Quantum Computing Breakthrough](https://www.scientificamerican.com/article/google-makes-a-major-quantum-computing-breakthrough/): Dec 9, 2024 — Researchers at Google created a silicon chip with 105 qubits, quantum counterparts to classical bits. Then they linked up multiple physical ... + +5. **Quantum Computing research updates** + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): Jan. 9, 2025 — Researchers have recently achieved a significant breakthrough in the development of next-generation carbon-based quantum materials, opening new ... + - [Quantum Computing - latest research news and features](https://phys.org/tags/quantum+computing/): All the latest science news on quantum computing from Phys.org. Find the latest news, advancements, and breakthroughs. + - [Quantum computing](https://news.mit.edu/topic/quantum-computing): MIT physicists predict exotic form of matter with potential for quantum computing. + - [The Quantum Insider: Quantum Computing News & Top Stories](https://thequantuminsider.com/): Find the latest Quantum Computing news, data, market research, and insights. To stay up to date with the quantum market click here! + - [Quantum Computing News -- ScienceDaily](https://www.sciencedaily.com/news/matter_energy/quantum_computing/): 9, 2025 — Researchers have recently achieved a significant breakthrough in the development of next-generation carbon-based quantum materials, opening new ... + - [Quantum Computing](https://research.ibm.com/quantum-computing): We're inventing what's next in quantum research. Explore our recent work, access unique toolkits, and discover the breadth of topics that matter to us. + - [Quantum information - Latest research and news](https://www.nature.com/subjects/quantum-information): Quantum information systems could be able to transmit data that is fundamentally secure and solve problems that are beyond the power of modern computers. Latest ... + - [Quantum Computing News](https://scitechdaily.com/tag/quantum-computing/): Quantum computing is an advanced field of computing that leverages the principles of quantum mechanics to process information in fundamentally different ways. + - [Quantum Computing in 2024: Breakthroughs, Challenges ...](https://microtime.com/quantum-computing-in-2024-breakthroughs-challenges-and-what-lies-ahead/): Sep 5, 2024 — 2024 has been a year of significant progress in the field of QC, with several key breakthroughs that are bringing us closer to realizing its potential. + +6. **Quantum Computing innovations** + - [Breakthroughs in Quantum Computing](https://www.wevolver.com/article/breakthroughs-in-quantum-computing): Aug 19, 2024 — Another groundbreaking advancement is the teleportation of quantum information over distances exceeding 1,200km, facilitated by the Micius ... + - [Quantum computing: What leaders need to know now](https://mitsloan.mit.edu/ideas-made-to-matter/quantum-computing-what-leaders-need-to-know-now): Jan 11, 2024 — Quantum computing applies the laws of quantum mechanics to simulate and solve complex problems that are too difficult for the current genre of ... + - [Quantum Industry Explained: Applications, Innovations & ...](https://thequantuminsider.com/2024/02/05/quantum-industry-explained-applications-innovations-challenges/): Feb 5, 2024 — Quantum technology offers significant potential for innovation in various sectors including computing, communications, and sensing. + - [10 Quantum Computing Applications & Examples to Know](https://builtin.com/hardware/quantum-computing-applications): 10 Quantum Computing Applications to Know · Artificial intelligence · Better batteries · Cleaner fertilization · Cybersecurity · Drug development · Electronic ... + - [Quantum Computing Is Coming Faster Than You Think](https://www.forbes.com/sites/tiriasresearch/2023/11/28/quantum-computing-is-coming-faster-than-you-think/): Nov 28, 2023 — Another reason is the continued advancements being made in quantum computing is improvements in quantum chips, control logic, systems, and ... + - [What Is Quantum Computing?](https://www.ibm.com/think/topics/quantum-computing): Aug 5, 2024 — Explore IBM Quantum's latest innovations, research breakthroughs, and career opportunities as we push the boundaries of quantum computing. + - [Exploring the Latest Quantum Computing Advancements in ...](https://firstignite.com/exploring-the-latest-quantum-computing-advancements-in-2024/): Jul 11, 2024 — In 2024, the quantum computing landscape is set to witness exciting innovations. Key trends include continued efforts toward quantum supremacy. + - [Quantum Computing | Advancement of Innovations](https://www.nvidia.com/en-us/solutions/quantum-computing/): To prepare for a quantum-accelerated future, governments, universities, and industries are investing in hardware, software, and algorithm development. + +7. **Quantum Computing trends** + - [Emerging Trends in Quantum Computing for Scientific and ...](https://www.zuken.com/us/blog/emerging-trends-in-quantum-computing-for-scientific-and-industrial-applications/): In this post, we'll discuss trends for scientific and industrial applications and learn how Zuken's CR-8000 is supporting this transition. + - [What is quantum computing?](https://www.mckinsey.com/featured-insights/mckinsey-explainers/what-is-quantum-computing): Apr 5, 2024 — Quantum computing has so much promise and momentum that McKinsey has identified it as one of the next big trends in tech. Quantum computing ... + - [Quantum Computing Explained: A Must-Read for Executives](https://www.gartner.com/en/articles/quantum-computing): Sep 20, 2024 — Learn how quantum computing and other technology trends align with your digital ambitions. Plus, how to integrate them into your strategic ... + - [What's next for quantum computing](https://www.technologyreview.com/2023/01/06/1066317/whats-next-for-quantum-computing/): Jan 6, 2023 — In 2023, progress in quantum computing will be defined less by big hardware announcements than by researchers consolidating years of hard work. + - [The Rise of Quantum Computing](https://www.mckinsey.com/featured-insights/the-rise-of-quantum-computing): Accelerating technological breakthroughs, increasing investment flows, start-up proliferation, and promises of capable quantum systems by 2030 signal it's time ... + - [Quantum Computing Market 2024-2044: Technology, ...](https://www.idtechex.com/en/research-report/quantum-computing-market-2024-2044-technology-trends-players-forecasts/996): 20-year market forecasts for quantum computer hardware by volume (i.e., number of systems sold) and revenue. Individual forecast lines are available for eight ... + - [Future of Quantum Computing & 7 QC trends in 2025](https://research.aimultiple.com/future-of-quantum-computing/): Jan 7, 2025 — Future of Quantum Computing & 7 QC trends in 2025 ... Quantum computing can be a game-changer in fields such as cryptography, chemistry, material ... + - [Quantum cloud computing: Trends and challenges](https://www.sciencedirect.com/science/article/pii/S2949948824000271): by M Golec · 2024 · Cited by 14 — This article presents the vision and challenges for the quantum cloud computing paradigm that will emerge with the integration of quantum and cloud computing. + - [The Top Six Quantum Computing Trends for 2024](https://ai-techpark.com/the-top-six-quantum-computing-trends-for-2024/): May 9, 2024 — The Top Six Quantum Computing Trends for 2024 · 1. Quantum-Sensing Technologies · 2. Quantum-Safe Cryptography · 3. Quantum Machine Learning · 4 ... + +8. **Quantum Computing developments** + - [The latest developments in quantum science and technology ...](https://pme.uchicago.edu/news/world-quantum-day-2024-latest-developments-quantum-science-and-technology): Apr 12, 2024 — Many more advancements in quantum technology are yet to come. Secure communication through metropolitan-scale entangled quantum networks, ... + - [Quantum Computers News](https://www.sciencedaily.com/news/computers_math/quantum_computers/): 9, 2025 — Researchers have recently achieved a significant breakthrough in the development of next-generation carbon-based quantum materials, opening new ... + - [Breakthroughs in Quantum Computing](https://www.wevolver.com/article/breakthroughs-in-quantum-computing): Aug 19, 2024 — Another exciting academic-led development in quantum computing is its application in simulating molecular structures at the atomic scale. This ... + - [Quantum Computing: Developments in the UK and US](https://www.insideprivacy.com/data-privacy/quantum-computing-developments-in-the-uk-and-us/): Aug 9, 2024 — This update focuses on how growing quantum sector investment in the UK and US is leading to the development and commercialization of quantum ... + - [Quantum computing: What leaders need to know now](https://mitsloan.mit.edu/ideas-made-to-matter/quantum-computing-what-leaders-need-to-know-now): Jan 11, 2024 — An overview of quantum computing ... The idea for building a system that leverages physics principles to simulate problems too difficult to model ... + - [Quantum computing technology pushes for IT advantage](https://www.techtarget.com/searchcio/feature/Quantum-computing-technology-pushes-for-IT-advantage): Nov 27, 2024 — Timeline showing quantum computing milestones. Quantum computing developments have shifted over the years from basic research to the ... + - [What's next for quantum computing](https://www.technologyreview.com/2023/01/06/1066317/whats-next-for-quantum-computing/): Jan 6, 2023 — In 2023, progress in quantum computing will be defined less by big hardware announcements than by researchers consolidating years of hard work. + - [Quantum Computing Is Coming Faster Than You Think](https://www.forbes.com/sites/tiriasresearch/2023/11/28/quantum-computing-is-coming-faster-than-you-think/): Nov 28, 2023 — Another reason is the continued advancements being made in quantum computing is improvements in quantum chips, control logic, systems, and ... + - [2025 Will See Huge Advances in Quantum Computing. So ...](https://thequantuminsider.com/2025/01/08/2025-will-see-huge-advances-in-quantum-computing-so-what-is-a-quantum-chip-and-how-does-it-work/): 7 days ago — Many experts are expecting big advance in quantum computing in 2025, but what is a quantum chip and how does it work? + +9. **Quantum Computing future** + - [How Quantum Will Transform the Future of 5 Industries](https://www.honeywell.com/us/en/news/2020/07/how-quantum-will-transform-the-future-of-5-industries): Quantum computing could identify the best places to embed sensors to capture the most meaningful data as well as speed up the machine learning process. Quantum ... + - [Unlocking the quantum future | MIT News](https://news.mit.edu/2024/hackathon-unlocking-quantum-future-0318): Mar 18, 2024 — Quantum computing is the next frontier for faster and more powerful computing technologies. It has the potential to better optimize routes ... + - [Future of Quantum Computing: Unlocking the Possibilities](https://thequantuminsider.com/2023/04/06/future-of-quantum-computing/): Apr 6, 2023 — The future of quantum computing is bright, with the potential to revolutionize fields ranging from medicine to finance to cybersecurity. + - [The future of quantum computing | The TechTank Podcast](https://www.brookings.edu/articles/the-future-of-quantum-computing-the-techtank-podcast/): Quantum computing promises to solve problems that are impossible for today's computers, including key problems in cryptography, drug discovery, finance, ... + - [NVIDIA GTC 2025: Quantum Day to Illuminate the Future of ...](https://blogs.nvidia.com/blog/gtc-2025-quantum-day/): 13 hours ago — NVIDIA is celebrating and exploring remarkable progress in quantum computing by announcing its first Quantum Day at GTC 2025 on March 20. + - [Future of Quantum Computing & 7 QC trends in 2025](https://research.aimultiple.com/future-of-quantum-computing/): Jan 7, 2025 — Future of Quantum Computing & 7 QC trends in 2025 ... Quantum computing can be a game-changer in fields such as cryptography, chemistry, material ... + - [Quantum Computing Is the Future, and Schools Need to ...](https://www.scientificamerican.com/article/quantum-computing-is-the-future-and-schools-need-to-catch-up/): Mar 15, 2023 — Quantum technology is the future, and quantum computing education is STEM education, as Charles Tahan, the director at the National Quantum ... \ No newline at end of file diff --git a/rate_tracker.py b/rate_tracker.py new file mode 100644 index 0000000..c38468d --- /dev/null +++ b/rate_tracker.py @@ -0,0 +1,97 @@ +import time +import math +from typing import Dict, List, TypedDict + +class TokenUsage(TypedDict): + timestamp: float + tokens: int + +class RateTrackerConfig(TypedDict): + requestsPerMinute: int + tokensPerMinute: int + maxTokensPerRequest: int + +class RateTracker: + def __init__(self, config: RateTrackerConfig): + self.config = { + 'requestsPerMinute': config.get('requestsPerMinute', 50), + 'tokensPerMinute': config.get('tokensPerMinute', 400000), + 'maxTokensPerRequest': config.get('maxTokensPerRequest', 4000) + } + self.request_history: List[float] = [] + self.token_history: List[TokenUsage] = [] + self.last_cleanup = time.time() + + def cleanup(self): + now = time.time() + one_minute_ago = now - 60 + + # Only cleanup if it's been at least 5 seconds since last cleanup + if now - self.last_cleanup < 5: + return + + self.request_history = [t for t in self.request_history if t > one_minute_ago] + self.token_history = [t for t in self.token_history if t['timestamp'] > one_minute_ago] + self.last_cleanup = now + + def get_current_usage(self) -> Dict[str, int]: + self.cleanup() + total_tokens = sum(usage['tokens'] for usage in self.token_history) + return { + 'requests': len(self.request_history), + 'tokens': total_tokens + } + + def can_make_request(self, estimated_tokens: int) -> bool: + self.cleanup() + + current_usage = self.get_current_usage() + within_request_limit = current_usage['requests'] < self.config['requestsPerMinute'] + within_token_limit = current_usage['tokens'] + estimated_tokens <= self.config['tokensPerMinute'] + within_max_tokens = estimated_tokens <= self.config['maxTokensPerRequest'] + + return within_request_limit and within_token_limit and within_max_tokens + + def get_wait_time(self, estimated_tokens: int) -> float: + if estimated_tokens > self.config['maxTokensPerRequest']: + return -1 # Cannot make request - exceeds max tokens per request + + self.cleanup() + current_usage = self.get_current_usage() + + if current_usage['requests'] == 0 and current_usage['tokens'] == 0: + return 0 # No waiting needed + + wait_time = 0 + + # Calculate wait time for request limit + if current_usage['requests'] >= self.config['requestsPerMinute']: + oldest_request = min(self.request_history) if self.request_history else time.time() + wait_time = max(wait_time, oldest_request + 60 - time.time()) + + # Calculate wait time for token limit + if current_usage['tokens'] + estimated_tokens > self.config['tokensPerMinute']: + oldest_token = min(t['timestamp'] for t in self.token_history) if self.token_history else time.time() + token_wait_time = oldest_token + 60 - time.time() + wait_time = max(wait_time, token_wait_time) + + return math.ceil(wait_time * 1000) # Convert to milliseconds + + def record_usage(self, tokens: int): + now = time.time() + self.request_history.append(now) + self.token_history.append({'timestamp': now, 'tokens': tokens}) + self.cleanup() + + def get_rate_limit_status(self) -> Dict[str, int]: + self.cleanup() + current_usage = self.get_current_usage() + oldest_timestamp = min(self.request_history) if self.request_history else time.time() + + return { + 'requestsRemaining': self.config['requestsPerMinute'] - current_usage['requests'], + 'tokensRemaining': self.config['tokensPerMinute'] - current_usage['tokens'], + 'requestsUsed': current_usage['requests'], + 'tokensUsed': current_usage['tokens'], + 'nextReset': oldest_timestamp + 60 + } \ No newline at end of file diff --git a/research-script.ts b/research-script.ts new file mode 100644 index 0000000..568b6e4 --- /dev/null +++ b/research-script.ts @@ -0,0 +1,211 @@ +import { ParallelSearchOrchestrator } from './src/core/parallel-orchestrator.js'; +import { ResearchConfig, ResearchResult, CategoryContent, ResearchData } from './src/types/research.js'; +import * as fs from 'fs'; + +export async function conductResearch(topic: string): Promise { + // Configure research parameters + // Read the research prompt file to get the structure + const promptFile = `${topic.toLowerCase().replace(/\s+/g, '-')}-research-prompt.txt`; + let categories: string[] = ['overview']; // Default category + + try { + if (fs.existsSync(promptFile)) { + const promptContent = fs.readFileSync(promptFile, 'utf-8'); + // Extract categories from numbered sections in the prompt + categories = promptContent + .split('\n') + .filter(line => /^\(\d+\)/.test(line)) // Lines starting with (1), (2), etc. + .map(line => { + // Extract main category from the section + const category = line.replace(/^\(\d+\)\s*Find\s+/, '') // Remove "(1) Find " + .split(/\s+and\s+|\s*,\s*/)[0] // Take first main topic + .toLowerCase() + .trim(); + return category; + }); + + if (categories.length === 0) { + categories = ['overview']; // Fallback if no categories found + } + } + } catch (error) { + console.warn('Could not read research prompt file, using default categories'); + } + + const config: ResearchConfig = { + maxDepth: 2, + maxUrlsPerDomain: 5, + relevanceThreshold: 0.3, + maxTotalUrls: 50, + timeout: 30000, + maxDynamicQueries: 10, + minConfidence: 0.7, + categories: categories, + excludeCategories: ['advertisements', 'comments', 'user-generated'], + maxContentAge: 365 * 24 * 60 * 60 * 1000 // 1 year in milliseconds + }; + + const orchestrator = new ParallelSearchOrchestrator(config); + console.log(`Initializing research for topic: ${topic}`); + + try { + // Initialize orchestrator + await orchestrator.initialize(); + console.log('Initialized research orchestrator'); + + // Execute deep research + console.log('Starting deep research...'); + const researchData = await orchestrator.executeDeepResearch(topic); + + // Create research result + const result: ResearchResult = { + topic, + timestamp: new Date().toISOString(), + data: researchData, + metadata: { + totalSources: researchData.sources.size, + categoryBreakdown: new Map( + Array.from(researchData.categories.entries()).map( + ([category, contents]) => [category, contents.length] + ) + ), + topEntities: researchData.entities + .sort((a, b) => b.confidence - a.confidence) + .slice(0, 10), + confidence: calculateOverallConfidence(researchData) + } + }; + + // Log research statistics + console.log('\n=== Research Statistics ==='); + console.log(`Total sources analyzed: ${result.metadata.totalSources}`); + console.log('Category breakdown:'); + result.metadata.categoryBreakdown.forEach((count, category) => { + console.log(`- ${category}: ${count} entries`); + }); + console.log(`Top entities identified: ${result.metadata.topEntities.length}`); + console.log(`Overall confidence score: ${result.metadata.confidence}`); + + return result; + + } catch (error) { + console.error('Research failed:', error); + throw error; + } finally { + // Cleanup resources + await orchestrator.cleanup(); + } +} + +function calculateOverallConfidence(data: ResearchData): number { + const categoryConfidences = Array.from(data.categories.values()) + .flat() + .map(content => content.relevance); + + const entityConfidences = data.entities.map(entity => entity.confidence); + const allConfidences = [...categoryConfidences, ...entityConfidences]; + + return allConfidences.length > 0 + ? allConfidences.reduce((sum, conf) => sum + conf, 0) / allConfidences.length + : 0; +} + +export function generateSummaryReport(results: ResearchResult): string { + const sections: string[] = []; + + // Add title and timestamp + sections.push(`Research Summary: ${results.topic}\n`); + sections.push(`Generated: ${new Date(results.timestamp).toLocaleString()}\n`); + sections.push(`Overall Confidence: ${(results.metadata.confidence * 100).toFixed(1)}%\n`); + + // Helper function to clean content + const cleanContent = (content: string): string => { + return content + .replace(/\s+/g, ' ') + .replace(/\[.*?\]/g, '') + .replace(/\(.*?\)/g, '') + .replace(/\{.*?\}/g, '') + .replace(/<.*?>/g, '') + .replace(/\b(?:edit|citation needed)\b/gi, '') + .replace(/\^/g, '') + .replace(/\n{3,}/g, '\n\n') + .trim(); + }; + + // Try to read the research prompt to structure the report + try { + const promptFile = `${results.topic.toLowerCase().replace(/\s+/g, '-')}-research-prompt.txt`; + if (fs.existsSync(promptFile)) { + const promptContent = fs.readFileSync(promptFile, 'utf-8'); + + // Extract main sections from the prompt + const mainSections = promptContent + .split('\n') + .filter(line => /^\(\d+\)/.test(line)) + .map(line => { + return line.replace(/^\(\d+\)\s*Find\s+/, '') + .split(/\s+and\s+|\s*,\s*/)[0] + .trim(); + }); + + // Process each section based on the prompt structure + mainSections.forEach(section => { + const sectionKey = section.toLowerCase(); + const relevantContent = Array.from(results.data.categories.entries()) + .filter(([category]) => category.includes(sectionKey)) + .flatMap(([_, contents]) => contents); + + if (relevantContent.length > 0) { + sections.push(`\n=== ${section} ===\n`); + + // Sort by relevance and take top entries + const sortedContent = relevantContent + .sort((a, b) => b.relevance - a.relevance) + .slice(0, 5); + + sortedContent.forEach(content => { + sections.push(`• ${cleanContent(content.content)}`); + }); + } + }); + } + } catch (error) { + // Fallback to default category-based organization + results.data.categories.forEach((contents, category) => { + if (contents.length > 0) { + sections.push(`\n=== ${category.charAt(0).toUpperCase() + category.slice(1)} ===\n`); + + const sortedContents = contents + .sort((a, b) => b.relevance - a.relevance) + .slice(0, 5); + + sortedContents.forEach(content => { + sections.push(`• ${cleanContent(content.content)}`); + }); + } + }); + } + + // Add key insights + if (results.data.keyInsights.length > 0) { + sections.push('\n=== Key Findings ===\n'); + results.data.keyInsights + .sort((a, b) => b.relevance - a.relevance) + .slice(0, 5) + .forEach(insight => { + sections.push(`• ${cleanContent(insight.content)}`); + }); + } + + // Add sources + if (results.data.sources.size > 0) { + sections.push('\n=== Sources ===\n'); + Array.from(results.data.sources) + .sort() + .forEach(source => sections.push(source)); + } + + return sections.join('\n'); +} + +export default conductResearch; \ No newline at end of file diff --git a/src/core/content-analyzer.ts b/src/core/content-analyzer.ts new file mode 100644 index 0000000..22725eb --- /dev/null +++ b/src/core/content-analyzer.ts @@ -0,0 +1,945 @@ +import natural from 'natural'; +import { ContentAnalysis, Topic, KeyPoint, Entity, EntityType, EntityMention, Relationship, Citation, ContentQuality, AnalysisOptions } from '../types/analysis.js'; +import { ExtractedContent } from '../types/content.js'; + +export class ContentAnalyzer { + private tokenizer: natural.WordTokenizer; + private tfidf: natural.TfIdf; + private stemmer: typeof natural.PorterStemmerFr; + private technicalTerms: Set; + private boilerplatePatterns: RegExp[]; + + private isTechnicalContent(text: string): boolean { + const technicalIndicators = [ + 'example', + 'implementation', + 'usage', + 'api', + 'method', + 'function', + 'parameter', + 'return', + 'class', + 'interface', + 'object', + 'pattern' + ]; + + const lowerText = text.toLowerCase(); + return technicalIndicators.some(indicator => lowerText.includes(indicator)) || + text.includes('```') || + /`[^`]+`/.test(text); + } + + private extractTechnicalTermsFromText(text: string): string[] { + const words = text.toLowerCase().split(/\W+/); + return words.filter(word => + word.length > 3 && + this.technicalTerms.has(word) && + !this.isStopWord(word) + ); + } + + constructor() { + this.tokenizer = new natural.WordTokenizer(); + this.tfidf = new natural.TfIdf(); + this.stemmer = natural.PorterStemmerFr; + + // Initialize technical terms focused on API wrappers and programming + this.technicalTerms = new Set([ + // API and Design Patterns + 'api', 'wrapper', 'client', 'sdk', 'library', 'interface', + 'endpoint', 'request', 'response', 'http', 'rest', 'soap', + 'facade', 'adapter', 'proxy', 'decorator', 'factory', + + // Implementation Concepts + 'implementation', 'method', 'function', 'class', 'object', + 'parameter', 'argument', 'return', 'async', 'await', 'promise', + 'callback', 'error', 'exception', 'handler', 'middleware', + + // Best Practices + 'pattern', 'practice', 'standard', 'convention', 'principle', + 'solid', 'dry', 'separation', 'concern', 'abstraction', + 'encapsulation', 'inheritance', 'polymorphism', + + // Testing and Quality + 'test', 'mock', 'stub', 'assertion', 'coverage', 'unit', + 'integration', 'validation', 'verification', 'documentation', + + // Common Features + 'authentication', 'authorization', 'security', 'cache', + 'rate', 'limit', 'throttle', 'retry', 'timeout', 'logging' + ]); + + // Initialize boilerplate patterns + this.boilerplatePatterns = [ + /copyright/i, + /all rights reserved/i, + /terms of service/i, + /privacy policy/i, + /cookie policy/i, + /contact us/i, + /about us/i, + /follow us/i, + /subscribe/i, + /sign up/i, + /log in/i, + /register/i + ]; + } + + public async analyze(content: ExtractedContent, options: AnalysisOptions = {}): Promise { + console.log('Starting content analysis for URL:', content.url); + console.log('Content length:', content.content.length); + + // Prepare content for analysis + const tokens = this.tokenizeContent(content.content); + this.tfidf.addDocument(tokens); + console.log('Tokenized content length:', tokens.length); + + // Extract topics and calculate relevance + console.log('Extracting topics...'); + const topics = await this.extractTopics(content, options); + console.log('Found topics:', topics.length, topics.map(t => t.name)); + + console.log('Extracting key points...'); + const keyPoints = this.extractKeyPoints(content, topics, options); + console.log('Found key points:', keyPoints.length); + + console.log('Extracting entities...'); + const entities = this.extractEntities(content); + console.log('Found entities:', entities.length); + + const relationships = this.findRelationships(entities, content); + const sentiment = this.analyzeSentiment(content.content); + const quality = this.assessQuality(content); + + // Merge similar topics + console.log('Merging similar topics...'); + const mergedTopics = this.mergeSimilarTopics(topics); + console.log('After merging:', mergedTopics.length, mergedTopics.map(t => t.name)); + + const result = { + relevanceScore: this.calculateRelevanceScore(content, mergedTopics), + topics: mergedTopics, + keyPoints: this.deduplicateKeyPoints(keyPoints), + entities, + sentiment, + relationships, + citations: this.extractCitations(content), + quality + }; + + console.log('Analysis complete. Topics:', result.topics.length); + console.log('Key points:', result.keyPoints.length); + console.log('Relevance score:', result.relevanceScore); + + return result; + } + + private tokenizeContent(text: string): string[] { + return this.tokenizer.tokenize(text.toLowerCase()) || []; + } + + private async extractTopics(content: ExtractedContent, options: AnalysisOptions): Promise { + console.log('Extracting topics from content...'); + const maxTopics = options.maxTopics || 8; + const minConfidence = options.minConfidence || 0.15; + + // Split content into sections + const sections = content.content.split(/\n\n+/); + console.log(`Found ${sections.length} sections to analyze`); + + // Initialize topic tracking + const topicMentions = new Map + }>(); + + // Enhanced topic indicators for quantum computing + const topicIndicators = [ + // General technical patterns + { pattern: /(?:using|implementing|creating)\s+(\w+(?:\s+\w+){0,2})\s+(?:pattern|approach|method)/i, weight: 1.2 }, + { pattern: /(?:best\s+practice|recommended)\s+(?:is|for)\s+(\w+(?:\s+\w+){0,2})/i, weight: 1.1 }, + { pattern: /(\w+(?:\s+\w+){0,2})\s+implementation/i, weight: 1.0 }, + { pattern: /(\w+(?:\s+\w+){0,2})\s+(?:wrapper|api|interface)/i, weight: 1.0 }, + + // Domain-specific patterns + { pattern: /(?:quantum)\s+(\w+(?:\s+\w+){0,2})/i, weight: 1.3 }, + { pattern: /(\w+(?:\s+\w+){0,2})\s+(?:qubit|qubits)/i, weight: 1.3 }, + { pattern: /(\w+(?:\s+\w+){0,2})\s+(?:algorithm|computation)/i, weight: 1.2 }, + { pattern: /(?:advances?|developments?|breakthroughs?)\s+in\s+(\w+(?:\s+\w+){0,2})/i, weight: 1.2 } + ]; + + // Analyze each section + sections.forEach((section, index) => { + console.log(`Analyzing section ${index + 1}...`); + const sectionLower = section.toLowerCase(); + + // Look for topic indicators + topicIndicators.forEach(({ pattern, weight }) => { + const matches = sectionLower.match(pattern); + if (matches && matches[1]) { + const topic = matches[1].trim(); + const existing = topicMentions.get(topic) || { count: 0, contexts: [], keywords: new Set() }; + existing.count += weight; + existing.contexts.push(section); + + // Extract related keywords + const keywords = this.extractKeywords(section); + keywords.forEach(k => existing.keywords.add(k)); + + topicMentions.set(topic, existing); + console.log(`Found topic: ${topic} (weight: ${weight})`); + } + }); + + // Look for technical content + if (this.isTechnicalContent(section)) { + const terms = this.extractTechnicalTermsFromText(section); + terms.forEach((term: string) => { + const existing = topicMentions.get(term) || { count: 0, contexts: [], keywords: new Set() }; + existing.count += 0.7; + existing.contexts.push(section); + topicMentions.set(term, existing); + }); + } + + // Look for code examples + if (section.includes('```') || section.includes('`')) { + const codeKeywords = this.extractCodeKeywords(section); + codeKeywords.forEach(keyword => { + const existing = topicMentions.get(keyword) || { count: 0, contexts: [], keywords: new Set() }; + existing.count += 0.8; + existing.contexts.push(section); + topicMentions.set(keyword, existing); + console.log(`Found code keyword: ${keyword}`); + }); + } + }); + + console.log(`Found ${topicMentions.size} potential topics`); + + // Convert to topics with enhanced scoring + const topics: Topic[] = Array.from(topicMentions.entries()) + .map(([name, data]) => { + // Calculate confidence with context bonus + let confidence = Math.min(1, data.count / 3); + + // Boost confidence for topics with multiple contexts + if (data.contexts.length > 1) { + confidence *= 1.2; + } + + // Boost confidence for topics with technical keywords + if (data.keywords.size > 2) { + confidence *= 1.1; + } + + return { + name, + confidence: Math.min(1, confidence), + keywords: Array.from(data.keywords) + }; + }) + .filter(topic => { + const meetsThreshold = topic.confidence >= minConfidence; + console.log(`Topic ${topic.name}: confidence ${topic.confidence} ${meetsThreshold ? 'accepted' : 'rejected'}`); + return meetsThreshold; + }) + .sort((a, b) => b.confidence - a.confidence) + .slice(0, maxTopics); + + console.log(`Extracted ${topics.length} topics above confidence threshold`); + return topics; + } + + private extractKeywords(text: string): string[] { + const words = text.toLowerCase().split(/\W+/); + return words.filter(word => + word.length > 3 && + this.technicalTerms.has(word) && + !this.isStopWord(word) + ); + } + + private extractCodeKeywords(text: string): string[] { + const codePatterns = [ + /class\s+(\w+)/g, + /function\s+(\w+)/g, + /method\s+(\w+)/g, + /interface\s+(\w+)/g, + /import\s+(\w+)/g, + /require\s+['"](.+?)['"]/g + ]; + + const keywords = new Set(); + codePatterns.forEach(pattern => { + let match; + while ((match = pattern.exec(text)) !== null) { + if (match[1]) { + keywords.add(match[1].toLowerCase()); + } + } + }); + + return Array.from(keywords); + } + + private getImportantTerms(text: string): Array<{term: string; score: number}> { + const terms: Array<{term: string; score: number}> = []; + const tokens = this.tokenizeContent(text); + + this.tfidf.listTerms(0).forEach(item => { + const term = item.term; + if (term.length > 2 && !this.isStopWord(term)) { + // Boost score for technical terms + const score = this.technicalTerms.has(term) ? item.tfidf * 1.5 : item.tfidf; + terms.push({ term, score }); + } + }); + + return terms.sort((a, b) => b.score - a.score); + } + + private mergeSimilarTopics(topics: Topic[]): Topic[] { + const merged: Topic[] = []; + const processed = new Set(); + + for (const topic of topics) { + if (processed.has(topic.name)) continue; + + // Find similar topics + const similar = topics.filter(t => + !processed.has(t.name) && + (this.areTopicsSimilar(topic, t) || this.areTopicsRelated(topic, t)) + ); + + if (similar.length > 0) { + // Merge topics + const mergedTopic: Topic = { + name: this.selectBestTopicName(similar.map(t => t.name)), + confidence: Math.max(...similar.map(t => t.confidence)), + keywords: Array.from(new Set(similar.flatMap(t => t.keywords))) + }; + merged.push(mergedTopic); + similar.forEach(t => processed.add(t.name)); + } else { + merged.push(topic); + processed.add(topic.name); + } + } + + return merged; + } + + private areTopicsSimilar(topic1: Topic, topic2: Topic): boolean { + // Check for stem similarity + const stem1 = this.stemmer.stem(topic1.name); + const stem2 = this.stemmer.stem(topic2.name); + if (stem1 === stem2) return true; + + // Check for keyword overlap + const keywords1 = new Set(topic1.keywords); + const keywords2 = new Set(topic2.keywords); + const overlap = [...keywords1].filter(k => keywords2.has(k)).length; + const similarity = overlap / Math.min(keywords1.size, keywords2.size); + return similarity > 0.5; + } + + private areTopicsRelated(topic1: Topic, topic2: Topic): boolean { + // Check if topics often appear together in technical contexts + const technicalPairs = [ + ['api', 'wrapper'], + ['wrapper', 'implementation'], + ['pattern', 'practice'], + ['method', 'interface'], + ['class', 'object'], + ['error', 'handling'], + ['authentication', 'security'] + ]; + + return technicalPairs.some(([t1, t2]) => + (topic1.name.toLowerCase().includes(t1) && topic2.name.toLowerCase().includes(t2)) || + (topic1.name.toLowerCase().includes(t2) && topic2.name.toLowerCase().includes(t1)) + ); + } + + private selectBestTopicName(names: string[]): string { + // Prefer technical terms + const technicalNames = names.filter(name => + this.technicalTerms.has(name.toLowerCase()) + ); + if (technicalNames.length > 0) { + return technicalNames[0]; + } + + // Otherwise use the longest name + return names.sort((a, b) => b.length - a.length)[0]; + } + + private areTermsRelated(term1: string, term2: string): boolean { + // Use word stems to check relation + const stem1 = this.stemmer.stem(term1); + const stem2 = this.stemmer.stem(term2); + + if (stem1 === stem2) return true; + + // Check technical term relationships + const technicalPairs = [ + ['api', 'wrapper'], + ['wrapper', 'implementation'], + ['pattern', 'practice'], + ['method', 'interface'], + ['class', 'object'], + ['error', 'handling'], + ['authentication', 'security'] + ]; + + return technicalPairs.some(([t1, t2]) => + (term1.includes(t1) && term2.includes(t2)) || + (term1.includes(t2) && term2.includes(t1)) + ); + } + + private selectTopicName(mainTerm: string, relatedTerms: string[]): string { + // Prefer technical terms + const technicalTerms = [mainTerm, ...relatedTerms].filter(term => + this.technicalTerms.has(term) + ); + + if (technicalTerms.length > 0) { + return technicalTerms[0].charAt(0).toUpperCase() + technicalTerms[0].slice(1); + } + + return mainTerm.charAt(0).toUpperCase() + mainTerm.slice(1); + } + + private extractKeyPoints(content: ExtractedContent, topics: Topic[], options: AnalysisOptions): KeyPoint[] { + // Split content into paragraphs first + const paragraphs = content.content.split(/\n\n+/); + const keyPoints: KeyPoint[] = []; + const minImportance = options.minImportance || 0.25; // Lowered threshold + + // First pass: identify best practice and implementation sections + const bestPracticeSections = paragraphs.filter(p => + /best\s+practices?|recommended|should|must|guidelines?/i.test(p) + ); + const implementationSections = paragraphs.filter(p => + /implementation|example|usage|how\s+to|approach/i.test(p) || + p.includes('```') || + /\b(function|class|method|interface)\b/.test(p) + ); + + // Process best practice sections + bestPracticeSections.forEach(section => { + const sentences = section.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 20); + sentences.forEach(sentence => { + if (this.isBestPracticeStatement(sentence)) { + const importance = this.calculateSentenceImportance(sentence, topics) * 1.3; // Boost best practices + if (importance >= minImportance) { + keyPoints.push({ + text: sentence.trim(), + importance, + topics: this.findRelatedTopics(sentence, topics), + supportingEvidence: this.findSupportingEvidence(sentence, content) + }); + } + } + }); + }); + + // Process implementation sections + implementationSections.forEach(section => { + const sentences = section.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 20); + sentences.forEach(sentence => { + if (this.isImplementationGuidance(sentence)) { + const importance = this.calculateSentenceImportance(sentence, topics) * 1.2; // Boost implementation guidance + if (importance >= minImportance) { + const evidence = [ + ...this.findSupportingEvidence(sentence, content), + ...this.extractCodeExamples(section) + ]; + keyPoints.push({ + text: sentence.trim(), + importance, + topics: this.findRelatedTopics(sentence, topics), + supportingEvidence: evidence + }); + } + } + }); + }); + + // Process remaining paragraphs for other insights + paragraphs.forEach(paragraph => { + if (!bestPracticeSections.includes(paragraph) && !implementationSections.includes(paragraph)) { + const sentences = paragraph.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 20); + sentences.forEach(sentence => { + const importance = this.calculateSentenceImportance(sentence, topics); + if (importance >= minImportance && this.isInsightful(sentence)) { + keyPoints.push({ + text: sentence.trim(), + importance, + topics: this.findRelatedTopics(sentence, topics), + supportingEvidence: this.findSupportingEvidence(sentence, content) + }); + } + }); + } + }); + + return this.deduplicateKeyPoints( + keyPoints.sort((a, b) => b.importance - a.importance) + .slice(0, options.maxKeyPoints || 15) + ); + } + + private isBestPracticeStatement(sentence: string): boolean { + const bestPracticeIndicators = [ + /\b(?:should|must|recommend|best|practice|important|key|essential|avoid|ensure)\b/i, + /\b(?:pattern|approach|strategy|technique|principle)\b/i, + /\b(?:better|improve|optimize|enhance)\b/i, + /\b(?:common|typical|standard|conventional)\b/i + ]; + + const lowerSentence = sentence.toLowerCase(); + return bestPracticeIndicators.some(pattern => pattern.test(lowerSentence)) && + !this.isBoilerplate(sentence); + } + + private isImplementationGuidance(sentence: string): boolean { + const implementationIndicators = [ + /\b(?:implement|create|build|develop|use|initialize|configure)\b/i, + /\b(?:method|function|class|interface|object)\b/i, + /\b(?:parameter|argument|return|value|type)\b/i, + /\b(?:example|sample|demo|code)\b/i + ]; + + const lowerSentence = sentence.toLowerCase(); + return implementationIndicators.some(pattern => pattern.test(lowerSentence)) && + !this.isBoilerplate(sentence); + } + + private isInsightful(sentence: string): boolean { + // Check if sentence contains meaningful technical content + const technicalTermCount = this.tokenizeContent(sentence) + .filter(token => this.technicalTerms.has(token)).length; + + return technicalTermCount >= 2 && // Has multiple technical terms + sentence.length > 30 && // Not too short + !this.isBoilerplate(sentence) && + !/^\s*[^a-zA-Z]*\s*$/.test(sentence); // Contains actual words + } + + private extractCodeExamples(text: string): string[] { + const examples: string[] = []; + + // Extract code blocks + const codeBlockRegex = /```[\s\S]*?```/g; + let match; + while ((match = codeBlockRegex.exec(text)) !== null) { + examples.push(match[0]); + } + + // Extract inline code + const inlineCodeRegex = /`[^`]+`/g; + while ((match = inlineCodeRegex.exec(text)) !== null) { + examples.push(match[0]); + } + + return examples; + } + + private deduplicateKeyPoints(keyPoints: KeyPoint[]): KeyPoint[] { + const unique: KeyPoint[] = []; + const seen = new Set(); + + for (const point of keyPoints) { + const normalized = this.normalizeText(point.text); + if (!seen.has(normalized) && !this.hasVerySimilarPoint(normalized, seen)) { + unique.push(point); + seen.add(normalized); + } + } + + return unique; + } + + private normalizeText(text: string): string { + return text.toLowerCase() + .replace(/\s+/g, ' ') + .replace(/[^\w\s]/g, '') + .trim(); + } + + private hasVerySimilarPoint(text: string, seen: Set): boolean { + for (const existing of seen) { + const similarity = this.calculateTextSimilarity(text, existing); + if (similarity > 0.8) return true; + } + return false; + } + + private calculateTextSimilarity(text1: string, text2: string): number { + const words1 = new Set(text1.split(' ')); + const words2 = new Set(text2.split(' ')); + const intersection = new Set([...words1].filter(x => words2.has(x))); + const union = new Set([...words1, ...words2]); + return intersection.size / union.size; + } + + private calculateSentenceImportance(sentence: string, topics: Topic[]): number { + const tokens = this.tokenizeContent(sentence); + let importance = 0; + let technicalTermCount = 0; + let hasCodeExample = false; + + // Check for code-like content + hasCodeExample = sentence.includes('```') || + sentence.includes('`') || + /\b(function|class|const|let|var|import|export)\b/.test(sentence); + + // Count technical terms with weighted categories + const termWeights = { + implementation: 1.2, // Implementation details + pattern: 1.2, // Design patterns + practice: 1.2, // Best practices + test: 1.1, // Testing related + error: 1.1, // Error handling + api: 1.3, // API specific + wrapper: 1.3, // Wrapper specific + method: 1.1, // Method related + class: 1.1 // Class related + }; + + tokens.forEach(token => { + if (this.technicalTerms.has(token)) { + technicalTermCount++; + // Apply additional weight for key terms + for (const [term, weight] of Object.entries(termWeights)) { + if (token.includes(term)) { + importance += weight - 1; // Add the extra weight + } + } + } + }); + + // Calculate topic relevance with reduced penalty for multiple topics + topics.forEach(topic => { + topic.keywords.forEach(keyword => { + if (tokens.includes(keyword.toLowerCase())) { + importance += topic.confidence * 0.8; // Reduced weight per topic + } + }); + }); + + // Boost importance based on technical term density + const technicalDensity = technicalTermCount / tokens.length; + importance += technicalDensity * 0.5; // Reduced multiplier + + // Boost for code examples + if (hasCodeExample) { + importance += 0.3; + } + + // Boost for sentences that look like best practices or implementation guidance + if ( + sentence.toLowerCase().includes('should') || + sentence.toLowerCase().includes('best practice') || + sentence.toLowerCase().includes('recommend') || + sentence.toLowerCase().includes('pattern') || + sentence.toLowerCase().includes('example') + ) { + importance += 0.2; + } + + return Math.min(importance, 1); + } + + private findRelatedTopics(sentence: string, topics: Topic[]): string[] { + const tokens = this.tokenizeContent(sentence); + return topics + .filter(topic => + topic.keywords.some(keyword => + tokens.includes(keyword.toLowerCase()) + ) + ) + .map(topic => topic.name); + } + + private findSupportingEvidence(sentence: string, content: ExtractedContent): string[] { + const tokens = this.tokenizeContent(sentence); + const evidence: string[] = []; + + // Split content into sentences + const sentences = content.content.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 0); + + // Find sentences that share significant terms with the input sentence + sentences.forEach(s => { + if (s === sentence) return; + + const sTokens = this.tokenizeContent(s); + const sharedTerms = tokens.filter(t => sTokens.includes(t)); + + // Check if the sentence contains technical terms + const hasTechnicalTerms = sTokens.some(t => this.technicalTerms.has(t)); + + if (sharedTerms.length >= 2 && hasTechnicalTerms) { + evidence.push(s); + } + }); + + return evidence; + } + + private extractEntities(content: ExtractedContent): Entity[] { + // Extract technical entities like algorithm names, standards, etc. + const entities: Entity[] = []; + const text = content.content; + + // Look for standard numbers (e.g., FIPS 203) + const standardRegex = /(?:FIPS|SP|RFC)\s+\d+(?:-\d+)?/g; + const standards = text.match(standardRegex) || []; + standards.forEach(standard => { + const mentions = this.findMentions(text, standard); + entities.push({ + name: standard, + type: 'standard' as EntityType, + mentions + }); + }); + + // Look for algorithm names + const algorithmRegex = /(?:ML-KEM|ML-DSA|SLH-DSA|CRYSTALS-Kyber|CRYSTALS-Dilithium|SPHINCS\+|FALCON)(?:-\d+)?/g; + const algorithms = text.match(algorithmRegex) || []; + algorithms.forEach(algorithm => { + const mentions = this.findMentions(text, algorithm); + entities.push({ + name: algorithm, + type: 'algorithm' as EntityType, + mentions + }); + }); + + return entities; + } + + private findMentions(text: string, term: string): EntityMention[] { + const mentions: EntityMention[] = []; + let pos = text.indexOf(term); + while (pos !== -1) { + const start = Math.max(0, pos - 50); + const end = Math.min(text.length, pos + term.length + 50); + mentions.push({ + text: term, + position: { + start: pos, + end: pos + term.length + }, + context: text.substring(start, end) + }); + pos = text.indexOf(term, pos + 1); + } + return mentions; + } + + private findRelationships(entities: Entity[], content: ExtractedContent): Relationship[] { + const relationships: Relationship[] = []; + const text = content.content; + + // Look for relationships between standards and algorithms + entities.forEach(e1 => { + if (e1.type === 'standard') { + entities.forEach(e2 => { + if (e2.type === 'algorithm') { + // Check if entities appear close to each other + const distance = this.findMinDistance(text, e1.name, e2.name); + if (distance < 100) { // within 100 characters + relationships.push({ + source: e1.name, + target: e2.name, + type: 'specifies', + confidence: 1 - (distance / 100) + }); + } + } + }); + } + }); + + return relationships; + } + + private findMinDistance(text: string, term1: string, term2: string): number { + let minDistance = Infinity; + let pos1 = text.indexOf(term1); + + while (pos1 !== -1) { + let pos2 = text.indexOf(term2); + while (pos2 !== -1) { + const distance = Math.abs(pos2 - pos1); + minDistance = Math.min(minDistance, distance); + pos2 = text.indexOf(term2, pos2 + 1); + } + pos1 = text.indexOf(term1, pos1 + 1); + } + + return minDistance; + } + + private analyzeSentiment(text: string) { + const analyzer = new natural.SentimentAnalyzer( + 'English', + natural.PorterStemmerFr, + 'afinn' + ); + + const tokens = this.tokenizeContent(text); + const score = analyzer.getSentiment(tokens); + + return { + score: Math.max(-1, Math.min(1, score)), // Normalize to [-1, 1] + confidence: Math.abs(score) / 5, // Simple confidence calculation + aspects: [] // Could be enhanced with aspect-based sentiment analysis + }; + } + + private assessQuality(content: ExtractedContent): ContentQuality { + return { + readability: this.calculateReadabilityScore(content.content), + informationDensity: this.calculateInformationDensity(content), + technicalDepth: this.calculateTechnicalDepth(content), + credibilityScore: this.calculateCredibilityScore(content), + freshness: this.calculateFreshnessScore(content) + }; + } + + private calculateReadabilityScore(text: string): number { + const sentences = text.split(/[.!?]+/).length; + const words = text.split(/\s+/).length; + const syllables = this.countSyllables(text); + + // Flesch-Kincaid Grade Level + const grade = 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59; + + // Convert to a 0-1 score, where 0.5 represents college level + return Math.max(0, Math.min(1, 1 - (grade / 20))); + } + + private countSyllables(text: string): number { + const words = text.split(/\s+/); + return words.reduce((count, word) => { + return count + this.countWordSyllables(word); + }, 0); + } + + private countWordSyllables(word: string): number { + word = word.toLowerCase(); + if (word.length <= 3) return 1; + + word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, ''); + word = word.replace(/^y/, ''); + + const syllables = word.match(/[aeiouy]{1,2}/g); + return syllables ? syllables.length : 1; + } + + private calculateInformationDensity(content: ExtractedContent): number { + const tokens = this.tokenizeContent(content.content); + const technicalTerms = tokens.filter(t => this.technicalTerms.has(t)); + return Math.min(1, technicalTerms.length / (tokens.length * 0.2)); + } + + private calculateTechnicalDepth(content: ExtractedContent): number { + const tokens = this.tokenizeContent(content.content); + const uniqueTechnicalTerms = new Set( + tokens.filter(t => this.technicalTerms.has(t)) + ); + return Math.min(1, uniqueTechnicalTerms.size / 20); + } + + private calculateCredibilityScore(content: ExtractedContent): number { + let score = 0.5; // Base score + + // Check for technical domain + if (content.url.includes('.gov') || + content.url.includes('.edu') || + content.url.includes('csrc.') || + content.url.includes('nist.')) { + score += 0.2; + } + + // Check for citations + const citations = this.extractCitations(content); + if (citations.length > 0) { + score += 0.1; + } + + // Check for technical content + const tokens = this.tokenizeContent(content.content); + const technicalTermRatio = tokens.filter(t => this.technicalTerms.has(t)).length / tokens.length; + score += technicalTermRatio * 0.2; + + return Math.min(1, score); + } + + private calculateFreshnessScore(content: ExtractedContent): number { + if (!content.metadata?.datePublished) return 0.5; + + const published = new Date(content.metadata.datePublished); + const now = new Date(); + const ageInDays = (now.getTime() - published.getTime()) / (1000 * 60 * 60 * 24); + + // Score decreases with age, but technical content stays relevant longer + return Math.max(0, Math.min(1, 1 - (ageInDays / 365))); + } + + private extractCitations(content: ExtractedContent): Citation[] { + const citations: Citation[] = []; + const text = content.content; + + // Look for standard references + const standardRefs = text.match(/(?:FIPS|SP|RFC)\s+\d+(?:-\d+)?/g) || []; + standardRefs.forEach(ref => { + citations.push({ + text: ref, + type: 'standard' + }); + }); + + // Look for URL citations + const urls = text.match(/https?:\/\/[^\s)]+/g) || []; + urls.forEach(url => { + citations.push({ + text: url, + type: 'url', + source: url + }); + }); + + return citations; + } + + private isStopWord(word: string): boolean { + return natural.stopwords.includes(word.toLowerCase()); + } + + private calculateRelevanceScore(content: ExtractedContent, topics: Topic[]): number { + // Calculate overall relevance based on topics and content quality + const topicScore = topics.reduce((sum, topic) => sum + topic.confidence, 0) / (topics.length || 1); + const quality = this.assessQuality(content); + + return Math.min( + 1, + (topicScore * 0.6) + + (quality.technicalDepth * 0.2) + + (quality.informationDensity * 0.2) + ); + } + + private isBoilerplate(text: string): boolean { + return this.boilerplatePatterns.some(pattern => pattern.test(text)); + } +} \ No newline at end of file diff --git a/src/core/content-extractor.ts b/src/core/content-extractor.ts new file mode 100644 index 0000000..933d0d5 --- /dev/null +++ b/src/core/content-extractor.ts @@ -0,0 +1,779 @@ +import * as cheerio from 'cheerio'; +import htmlToMd from 'html-to-md'; +import { ExtractedContent, ContentMetadata, ContentSection, ContentExtractionOptions } from '../types/content.js'; + +type CheerioRoot = ReturnType; + +export class ContentExtractor { + private technicalSelectors = [ + // Code blocks and examples + 'pre', 'code', '.example', '.code-example', + // API and implementation details + '.api-details', '.implementation-details', + '.method-signature', '.function-signature', + // Parameters and documentation + '.parameters', '.returns', '.arguments', + '.technical-docs', '.api-docs' + ]; + + private boilerplateSelectors = [ + // Navigation elements + 'nav', 'header', 'footer', + // Social sharing + '.social-share', '.share-buttons', '[id*="share"]', '[class*="share"]', + // Navigation menus + '.menu', '.navigation', '#menu', '#nav', + // Sidebars + '.sidebar', '#sidebar', '[class*="sidebar"]', + // Comments + '#comments', '.comments', '.comment-section', + // Advertisements + '.ad', '.ads', '.advertisement', '[id*="ad-"]', '[class*="ad-"]', + // Popups and overlays + '.popup', '.modal', '.overlay', + // Common UI elements + '.header-content', '.footer-content', '.site-header', '.site-footer', + // Cookie notices and banners + '.cookie-notice', '.cookie-banner', '.gdpr', '[class*="cookie"]', '[id*="cookie"]', + // Search and related content + '.search', '.search-form', '.related-posts', '.related-articles', + // Common widget areas + '.widget', '.widgets', '[class*="widget"]', + // Newsletter and subscription forms + '.newsletter', '.subscribe', '[class*="newsletter"]', '[class*="subscribe"]', + // Social media elements + '.social', '.social-media', '[class*="social"]', + // Print and utility links + '.print', '.utility-nav', '[class*="print"]', + // Common dynamic elements + '[data-widget]', '[data-module]', + // Common tracking and analytics + '[data-analytics]', '[data-tracking]', + // Additional UI elements + 'button', '[role="button"]', '.button', '.btn', + // Footer-like elements + '[class*="footer"]', '[id*="footer"]', 'c4d-footer', 'c4d-footer-container', + // Navigation-like elements + '[class*="nav"]', '[id*="nav"]', 'c4d-nav', + // Legal and policy elements + '[class*="legal"]', '[id*="legal"]', '[class*="policy"]', '[id*="policy"]', + // Common web components + 'c4d-*', + // Additional cookie-related elements + '[class*="cookie-preferences"]', '[id*="cookie-preferences"]', + '[class*="cookie-settings"]', '[id*="cookie-settings"]', + '[class*="cookie-consent"]', '[id*="cookie-consent"]', + // Additional button-related elements + '[class*="btn-"]', '[id*="btn-"]', '[class*="button-"]', '[id*="button-"]', + // Additional navigation elements + '[class*="menu-"]', '[id*="menu-"]', '[class*="navigation-"]', '[id*="navigation-"]', + // Additional footer elements + '[class*="bottom-"]', '[id*="bottom-"]', '[class*="foot-"]', '[id*="foot-"]' + ]; + + private htmlToMarkdownOptions = { + skipTags: [], // Don't skip any tags by default + emDelimiter: '_', + bulletListMarker: '-', + codeBlockStyle: 'fenced', + headingStyle: 'atx', + keepReplacement: true, + keepHtml: false, + listStyle: 'dash', + codeStyle: 'fenced', + customRules: [ + // Custom rule for links + { + selector: 'a', + replacement: (content: string, node: any) => { + const href = node.getAttribute('href'); + // Only preserve external links + if (href && href.startsWith('http')) { + return `[${content}](${href})`; + } + return content; + } + }, + // Custom rule for images + { + selector: 'img', + replacement: (content: string, node: any) => { + const alt = node.getAttribute('alt'); + return alt ? `[Image: ${alt}]` : ''; + } + }, + // Custom rule for tables + { + selector: 'table', + replacement: (content: string, node: any) => { + return this.convertTableToMarkdown(node); + } + } + ] + }; + + private convertTableToMarkdown(tableNode: any): string { + const $ = cheerio.load(tableNode); + let markdown = '\n'; + + // Get all rows including header row + const rows = $('tr').toArray(); + if (rows.length === 0) return ''; + + // Get maximum number of columns + const maxColumns = Math.max(...rows.map(row => $(row).find('th, td').length)); + if (maxColumns === 0) return ''; + + // Process headers + const headerRow = $(rows[0]); + const headers: string[] = []; + headerRow.find('th, td').each((_, cell) => { + headers.push($(cell).text().trim() || ' '); + }); + // Pad headers if needed + while (headers.length < maxColumns) { + headers.push(' '); + } + + // Create header row + markdown += '| ' + headers.join(' | ') + ' |\n'; + // Create separator row with proper alignment + markdown += '|' + Array(maxColumns).fill(' --- ').join('|') + '|\n'; + + // Process data rows (skip first row if it was header) + for (let i = headerRow.find('th').length > 0 ? 1 : 0; i < rows.length; i++) { + const cells: string[] = []; + $(rows[i]).find('td').each((_, cell) => { + cells.push($(cell).text().trim() || ' '); + }); + // Pad cells if needed + while (cells.length < maxColumns) { + cells.push(' '); + } + markdown += '| ' + cells.join(' | ') + ' |\n'; + } + + return markdown + '\n'; + } + + public async extract(html: string, url: string, options: ContentExtractionOptions = {}): Promise { + console.log('Starting content extraction for URL:', url); + console.log('Initial HTML length:', html.length); + + const $ = cheerio.load(html); + console.log('DOM loaded successfully'); + + // Remove unwanted elements + console.log('Cleaning up DOM...'); + this.cleanupDOM($); + console.log('DOM cleanup complete'); + + // Extract metadata + console.log('Extracting metadata...'); + const metadata = this.extractMetadata($); + console.log('Metadata extracted:', metadata); + + // Extract main content sections + console.log('Extracting content sections...'); + const sections = this.extractContentSections($); + console.log('Found sections:', sections.length); + sections.forEach((section, index) => { + console.log(`Section ${index + 1}:`, { + id: section.id, + type: section.type, + title: section.title, + importance: section.importance, + contentLength: section.content.length + }); + }); + + // Extract structured data + const structuredData = options.extractStructuredData ? + this.extractStructuredData($) : undefined; + + // Convert content to markdown + console.log('Converting content to markdown...'); + const mainContent = sections + .map(section => section.content) + .join('\n\n'); + + const content = htmlToMd(mainContent, this.htmlToMarkdownOptions); + console.log('Markdown conversion complete. Length:', content.length); + + // Clean up and format the content + console.log('Cleaning and formatting content...'); + const cleanedContent = this.cleanContent(this.formatMarkdown(content)); + console.log('Content cleanup complete. Final length:', cleanedContent.length); + + const title = this.extractTitle($); + console.log('Extracted title:', title); + + const result = { + url, + title, + content: this.truncateContent(cleanedContent, options.maxContentLength), + html: options.includeHtml ? html : undefined, + timestamp: new Date().toISOString(), + metadata, + structuredData + }; + + console.log('Content extraction complete'); + return result; + } + + private cleanupDOM($: CheerioRoot): void { + console.log('Starting DOM cleanup...'); + + // First pass: Remove obvious non-content elements + $('script, style, noscript, iframe, form, link, meta').remove(); + $('[style*="display: none"], [style*="display:none"], [hidden]').remove(); + + // Second pass: Identify and preserve main content areas + const mainContentSelectors = [ + 'article', + '[role="main"]', + 'main', + '.main-content', + '#main-content', + '.post-content', + '.article-content', + '.entry-content', + '.content', + '.documentation', + '.markdown-body' + ]; + + let mainContent = $('body'); + for (const selector of mainContentSelectors) { + const element = $(selector); + if (element.length > 0) { + mainContent = element; + console.log(`Found main content using selector: ${selector}`); + break; + } + } + + // Third pass: Remove boilerplate from main content + this.boilerplateSelectors.forEach(selector => { + mainContent.find(selector).each((_, elem) => { + const $elem = $(elem); + if (!this.containsTechnicalContent($elem)) { + $elem.remove(); + } + }); + }); + + // Fourth pass: Clean up remaining elements + mainContent.find('*').each((_, elem) => { + const $elem = $(elem); + const text = $elem.text().trim(); + + // Skip if element contains technical content + if (this.containsTechnicalContent($elem)) { + return; + } + + // Remove elements that are clearly UI components + if ( + text.match(/^(close|dismiss|accept|cancel|loading|\d+ min read|share|menu|search)$/i) || + text.match(/^(follow us|subscribe|sign up|log in|register)$/i) || + text.match(/^(cookie|privacy|terms|gdpr)/i) + ) { + $elem.remove(); + return; + } + + // Remove empty elements except code blocks + if (!$elem.is('pre, code') && text === '' && !$elem.find('img').length) { + $elem.remove(); + } + }); + + // Fifth pass: Remove duplicate content but preserve code blocks + const seen = new Set(); + mainContent.find('p, li, td, div').each((_, elem) => { + const $elem = $(elem); + if (this.containsTechnicalContent($elem)) { + return; // Don't deduplicate technical content + } + const text = $elem.text().trim(); + if (text && seen.has(text)) { + $elem.remove(); + } else { + seen.add(text); + } + }); + + // Replace body content with cleaned main content + $('body').empty().append(mainContent); + console.log('DOM cleanup complete'); + } + + private containsTechnicalContent($elem: cheerio.Cheerio): boolean { + // Check if element matches technical selectors + if (this.technicalSelectors.some(selector => $elem.is(selector))) { + return true; + } + + // Check if element contains code blocks + if ($elem.find('pre, code').length > 0) { + return true; + } + + // Check for technical keywords in text + const text = $elem.text().toLowerCase(); + return ( + text.includes('example') || + text.includes('implementation') || + text.includes('usage') || + text.includes('api') || + text.includes('method') || + text.includes('function') || + text.includes('parameter') || + text.includes('return') || + text.includes('class') || + text.includes('interface') || + text.includes('object') || + text.includes('pattern') + ); + } + + private cleanContent(content: string): string { + return content + // Remove duplicate newlines + .replace(/\n{3,}/g, '\n\n') + // Remove lines that are just special characters or very short + .split('\n') + .filter(line => { + const trimmed = line.trim(); + if (trimmed.length < 3) return false; + if (/^[-_=*#]+$/.test(trimmed)) return false; + return true; + }) + // Remove duplicate paragraphs + .filter((line, index, arr) => { + return arr.indexOf(line) === index; + }) + .join('\n'); + } + + private extractTitle($: CheerioRoot): string { + // Try OpenGraph title first + const ogTitle = $('meta[property="og:title"]').attr('content'); + if (ogTitle) return ogTitle; + + // Try article title + const articleTitle = $('article h1').first().text(); + if (articleTitle) return articleTitle; + + // Try main title + const mainTitle = $('h1').first().text() || $('title').text(); + if (mainTitle) return mainTitle; + + return 'Untitled'; + } + + private extractMetadata($: CheerioRoot): ContentMetadata { + const metadata: ContentMetadata = {}; + + // Extract author + metadata.author = + $('meta[name="author"]').attr('content') || + $('meta[property="article:author"]').attr('content') || + $('.author').first().text() || + $('[itemprop="author"]').first().text(); + + // Extract dates + metadata.datePublished = + $('meta[property="article:published_time"]').attr('content') || + $('meta[name="publication-date"]').attr('content') || + $('[itemprop="datePublished"]').attr('content'); + + metadata.lastModified = + $('meta[property="article:modified_time"]').attr('content') || + $('[itemprop="dateModified"]').attr('content'); + + // Extract language + metadata.language = $('html').attr('lang') || undefined; + + // Calculate reading time and word count + const text = $('body').text(); + const words = text.trim().split(/\s+/).length; + metadata.wordCount = words; + metadata.readingTime = Math.ceil(words / 200); // Assuming 200 words per minute + + return metadata; + } + + private extractContentSections($: CheerioRoot): ContentSection[] { + console.log('Starting content section extraction...'); + const sections: ContentSection[] = []; + + // Enhanced main content selectors with scoring + const mainSelectors = [ + { selector: 'article[class*="content"]', score: 10 }, + { selector: '[role="main"]', score: 9 }, + { selector: 'main', score: 8 }, + { selector: '.main-content', score: 8 }, + { selector: '#main-content', score: 8 }, + { selector: '.post-content', score: 7 }, + { selector: '.article-content', score: 7 }, + { selector: '.entry-content', score: 7 }, + { selector: '.content', score: 6 }, + { selector: '.documentation', score: 8 }, + { selector: '.markdown-body', score: 7 }, + { selector: '[itemprop="articleBody"]', score: 8 }, + { selector: '[data-content-type="article"]', score: 8 } + ]; + + // Find best content container based on scoring + let bestScore = 0; + let mainContent: cheerio.Cheerio = $('body'); + + mainSelectors.forEach(({ selector, score }) => { + const elements = $(selector); + elements.each((_, element) => { + const $element = $(element); + let elementScore = score; + + // Boost score based on content quality + elementScore += this.evaluateContentQuality($element); + + if (elementScore > bestScore) { + bestScore = elementScore; + mainContent = $element; + console.log(`Found better content container: ${selector} (score: ${elementScore})`); + } + }); + }); + + // Clean up the selected content container + this.cleanupContentContainer($, mainContent); + + // Extract sections based on semantic structure + let currentSection: ContentSection = { + id: 'main', + content: '', + importance: 1, + type: 'main' + }; + + // Process content hierarchically + mainContent.find('h1, h2, h3, h4, h5, h6, p, pre, code, .example, .implementation, .method, .function, section, article').each((_, element) => { + const $element = $(element); + const text = $element.text().trim(); + + if (!text) return; + + // Check for section breaks + const isHeading = $element.is('h1, h2, h3, h4, h5, h6'); + const isTechnical = this.containsTechnicalContent($element); + const isNewSection = $element.is('section, article') && $element.find('h1, h2, h3, h4, h5, h6').length > 0; + + if (isHeading || isTechnical || isNewSection) { + // Save current section if it has content + if (currentSection.content.trim()) { + sections.push(currentSection); + } + + // Calculate importance + const importance = this.calculateSectionImportance($element, isHeading, isTechnical); + + // Create new section + currentSection = { + id: `section-${sections.length + 1}`, + title: isHeading ? text : (isTechnical ? 'Technical Content' : 'Content Section'), + content: '', + importance, + type: isTechnical ? 'technical' : 'main' + }; + } + + // Add content to current section + if (isTechnical) { + // Include context for technical content + const context = this.getContextualContent($, $element); + currentSection.content += '\n' + (context || $element.html() || ''); + } else { + currentSection.content += '\n' + ($element.html() || ''); + } + }); + + // Add final section + if (currentSection.content.trim()) { + sections.push(currentSection); + } + + console.log(`Extracted ${sections.length} content sections`); + return sections; + } + + private evaluateContentQuality($element: cheerio.Cheerio): number { + let score = 0; + + // Check for technical content density + const text = $element.text(); + const technicalTerms = text.match(/\b(api|function|method|class|interface|example|implementation|code|return|parameter)\b/gi); + if (technicalTerms) { + score += technicalTerms.length * 0.5; + } + + // Check for code blocks + score += $element.find('pre, code').length * 2; + + // Check for proper content structure + score += $element.find('h1, h2, h3, h4, h5, h6').length; + score += $element.find('p').length * 0.5; + score += $element.find('ul, ol').length; + + // Penalize for common boilerplate + score -= $element.find(this.boilerplateSelectors.join(', ')).length * 2; + + return score; + } + + private calculateSectionImportance($element: cheerio.Cheerio, isHeading: boolean, isTechnical: boolean): number { + let importance = 0.5; + + if (isHeading) { + const level = parseInt($element.prop('tagName').slice(1)); + importance = Math.max(0.5, 1 - (level - 1) * 0.1); + } + + if (isTechnical) { + importance += 0.3; + } + + // Boost importance based on content quality + const contentQuality = this.evaluateContentQuality($element); + importance += Math.min(0.2, contentQuality * 0.05); + + return Math.min(1, importance); + } + + private findContextContainer($: CheerioRoot, $element: cheerio.Cheerio): cheerio.Cheerio { + // Look for the nearest container that provides context + let $container = $element; + let depth = 0; + const maxDepth = 3; // Prevent going too far up the DOM + + while (depth < maxDepth) { + const $parent = $container.parent(); + if (!$parent.length) break; + + // Check if parent provides good context + const parentText = $parent.text().trim(); + const hasContext = parentText.length > $container.text().length * 1.5 && + this.containsTechnicalContent($parent); + + if (hasContext) { + $container = $parent; + } + + depth++; + } + + return $container; + } + + private getContextualContent($: CheerioRoot, $element: cheerio.Cheerio): string | null { + const container = this.findContextContainer($, $element); + if (!container.length) return null; + + // Get previous sibling if it's a heading or description + let content = ''; + const $prevSibling = container.prev(); + if ($prevSibling.is('h1, h2, h3, h4, p') && + this.containsTechnicalContent($prevSibling)) { + content += $prevSibling.html() + '\n'; + } + + content += container.html() || ''; + + // Get next sibling if it provides additional context + const $nextSibling = container.next(); + if ($nextSibling.is('p') && + this.containsTechnicalContent($nextSibling)) { + content += '\n' + $nextSibling.html(); + } + + return content; + } + + private calculateImportance($element: cheerio.Cheerio): number { + let importance = 0.5; + + // Base importance on heading level + if ($element.is('h1')) importance = 1; + else if ($element.is('h2')) importance = 0.8; + else if ($element.is('h3')) importance = 0.6; + + // Increase importance based on content indicators + const text = $element.text().toLowerCase(); + if ( + text.includes('example') || + text.includes('implementation') || + text.includes('usage') || + text.includes('api') || + text.includes('method') || + text.includes('function') || + text.includes('parameter') || + text.includes('return') + ) { + importance += 0.2; + } + + // Increase importance if contains code + if ($element.find('code').length > 0 || $element.is('pre')) { + importance += 0.2; + } + + // Increase importance for technical elements + if ($element.is(this.technicalSelectors.join(','))) { + importance += 0.1; + } + + return Math.min(importance, 1); + } + + private extractStructuredData($: CheerioRoot): any[] { + const structuredData: any[] = []; + + // Extract JSON-LD + $('script[type="application/ld+json"]').each((_, element) => { + try { + const data = JSON.parse($(element).html() || '{}'); + structuredData.push(data); + } catch (error) { + // Ignore invalid JSON + } + }); + + return structuredData; + } + + private formatMarkdown(content: string): string { + // First pass: Basic cleanup + let formatted = content + // Fix list markers + .replace(/^\* /gm, '- ') + // Add spacing around headers + .replace(/^(#{1,6} .+)$/gm, '\n$1\n') + // Add spacing around lists + .replace(/^(- .+)$/gm, '$1\n'); + + // Handle code blocks + formatted = formatted.replace(/`([^`]+)`/g, (match, code) => { + if (code.includes('\n') || code.includes('function')) { + return '\n\n```\n' + code.trim() + '\n```\n\n'; + } + return '`' + code.trim() + '`'; + }); + + // Add spacing between sections + formatted = formatted.replace(/^(#{1,6} .*)/gm, '\n\n$1\n'); + + // Handle tables - complete rewrite of table structure + formatted = formatted.replace(/\|(.*)\|\n/g, (match: string, row: string) => { + const cells = row.split('|').map((cell: string) => cell.trim()).filter((cell: string) => cell); + if (cells.length === 0) return ''; + + // Detect if this is a separator row + if (cells.every(cell => /^[-\s]+$/.test(cell))) { + return ''; // Skip separator rows, we'll add our own + } + + // Check if this is a header row (no separator row seen yet) + if (!formatted.includes('| ---')) { + const separator = cells.map(() => '---').join(' | '); + return '| ' + cells.join(' | ') + ' |\n| ' + separator + ' |\n'; + } + + return '| ' + cells.join(' | ') + ' |\n'; + }); + + // Final cleanup + return formatted + // Fix paragraph spacing + .replace(/\n{3,}/g, '\n\n') + // Ensure sections are properly separated + .replace(/(\w)\n(#{1,6} )/g, '$1\n\n$2') + // Add proper spacing around code blocks + .replace(/```/g, '\n```\n') + .replace(/\n{4,}/g, '\n\n\n') + .trim(); + } + + private cleanupContentContainer($: CheerioRoot, $container: cheerio.Cheerio): void { + console.log('Cleaning up content container...'); + + // Remove nested boilerplate elements + this.boilerplateSelectors.forEach(selector => { + $container.find(selector).each((_, elem) => { + const $elem = $(elem); + // Keep element if it contains technical content + if (!this.containsTechnicalContent($elem)) { + $elem.remove(); + } + }); + }); + + // Remove empty elements + $container.find('*').each((_, elem) => { + const $elem = $(elem); + const text = $elem.text().trim(); + + // Skip technical content and elements with images + if (this.containsTechnicalContent($elem) || $elem.find('img').length > 0) { + return; + } + + // Remove if empty or just whitespace + if (!text || text.length < 3) { + $elem.remove(); + return; + } + + // Remove common UI text patterns + if ( + text.match(/^(close|dismiss|accept|cancel|loading|\d+ min read|share|menu|search)$/i) || + text.match(/^(follow us|subscribe|sign up|log in|register)$/i) || + text.match(/^(cookie|privacy|terms|gdpr)/i) + ) { + $elem.remove(); + } + }); + + // Remove duplicate content + const seen = new Set(); + $container.find('p, li, td, div').each((_, elem) => { + const $elem = $(elem); + + // Skip technical content + if (this.containsTechnicalContent($elem)) { + return; + } + + const text = $elem.text().trim(); + if (text && seen.has(text)) { + $elem.remove(); + } else { + seen.add(text); + } + }); + + console.log('Content container cleanup complete'); + } + + private truncateContent(content: string, maxLength?: number): string { + if (!maxLength || content.length <= maxLength) { + return content; + } + + // Truncate at word boundary + const truncated = content.slice(0, maxLength); + const lastSpace = truncated.lastIndexOf(' '); + return truncated.slice(0, lastSpace) + '...'; + } +} \ No newline at end of file diff --git a/src/core/research-session.ts b/src/core/research-session.ts new file mode 100644 index 0000000..c7fc673 --- /dev/null +++ b/src/core/research-session.ts @@ -0,0 +1,460 @@ +import { ResearchSession as IResearchSession, ResearchPlan, ResearchStep, ResearchProgress, ResearchFindings, StepResult, SessionOptions, Evidence } from '../types/session.js'; +import { ContentExtractor } from './content-extractor.js'; +import { ContentAnalyzer } from './content-analyzer.js'; +import { ExtractedContent } from '../types/content.js'; +import { ContentAnalysis } from '../types/analysis.js'; +import { chromium, Browser, BrowserContext } from 'playwright'; +import { parse as parseUrl } from 'url'; + +export class ResearchSession implements IResearchSession { + public id: string; + public topic: string; + public status: 'planning' | 'in_progress' | 'analyzing' | 'synthesizing' | 'completed' | 'failed' | 'cancelled'; + public plan: ResearchPlan; + public progress: ResearchProgress; + public findings: ResearchFindings; + public timestamp: { + created: string; + updated: string; + completed?: string; + }; + + private visitedUrls: Set; + private contentExtractor: ContentExtractor; + private contentAnalyzer: ContentAnalyzer; + private options: Required; + private browser: Browser | null = null; + private context: BrowserContext | null = null; + private startTime: number; + + private checkTimeout(): void { + const elapsed = Date.now() - this.startTime; + if (elapsed >= this.options.timeout) { + throw new Error('Research session timeout'); + } + } + + constructor(topic: string, options: SessionOptions = {}) { + this.id = `research_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + this.topic = topic; + this.status = 'planning'; + this.visitedUrls = new Set(); + this.contentExtractor = new ContentExtractor(); + this.contentAnalyzer = new ContentAnalyzer(); + this.startTime = Date.now(); + + this.options = { + maxSteps: options.maxSteps || 10, + maxDepth: options.maxDepth || 2, + maxBranching: options.maxBranching || 3, + timeout: options.timeout || 55000, // Set below MCP timeout + minRelevanceScore: options.minRelevanceScore || 0.7, + maxParallelOperations: options.maxParallelOperations || 3 + }; + + this.plan = this.createInitialPlan(); + this.progress = this.initializeProgress(); + this.findings = this.initializeFindings(); + this.timestamp = { + created: new Date().toISOString(), + updated: new Date().toISOString() + }; + } + + private async initializeBrowser(): Promise { + if (!this.browser) { + this.browser = await chromium.launch({ headless: true }); + this.context = await this.browser.newContext({ + userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + viewport: { width: 1280, height: 800 }, + deviceScaleFactor: 1, + isMobile: false, + hasTouch: false + }); + } + } + + private isProcessableUrl(url: string): boolean { + try { + const parsedUrl = parseUrl(url); + const path = parsedUrl.pathname?.toLowerCase() || ''; + + // Skip PDFs and other non-HTML content + const skipExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx']; + if (skipExtensions.some(ext => path.endsWith(ext))) { + console.error(`Skipping non-HTML content: ${url}`); + return false; + } + + return true; + } catch (error) { + console.error(`Invalid URL: ${url}`); + return false; + } + } + + private async fetchContent(url: string): Promise { + this.checkTimeout(); + + if (!this.isProcessableUrl(url)) { + throw new Error(`Cannot process URL: ${url}`); + } + + await this.initializeBrowser(); + if (!this.context) throw new Error('Browser context not initialized'); + + const page = await this.context.newPage(); + try { + // Navigate to the URL with a reduced timeout + await page.goto(url, { + waitUntil: 'domcontentloaded', + timeout: 10000 // 10 seconds max for page load + }); + + // Get the HTML content immediately without waiting for additional content + const html = await page.content(); + return html; + } catch (error) { + console.error(`Error fetching content from ${url}:`, error); + throw error; + } finally { + await page.close(); + } + } + + public async processUrl(url: string, depth: number = 0): Promise { + console.log(`Processing URL: ${url} at depth ${depth}`); + + if (this.visitedUrls.has(url)) { + console.log(`URL already visited: ${url}`); + return { searchResults: [] }; + } + + try { + console.log('Fetching content...'); + const htmlContent = await this.fetchContent(url); + console.log('Content fetched, length:', htmlContent.length); + + console.log('Extracting content...'); + const content = await this.contentExtractor.extract(htmlContent, url); + console.log('Content extracted, title:', content.title); + this.visitedUrls.add(url); + + console.log('Analyzing content...'); + const analysis = await this.contentAnalyzer.analyze(content); + console.log('Analysis complete:', { + topics: analysis.topics.length, + keyPoints: analysis.keyPoints.length, + relevanceScore: analysis.relevanceScore + }); + + // Update progress + this.progress.processedContent++; + this.progress.visitedUrls.add(url); + this.updateTimestamp(); + + console.log('Processing findings...'); + await this.processFindings(content, analysis, depth); + console.log('Findings processed'); + + const result = { + searchResults: [{ + url, + title: content.title, + snippet: content.content.substring(0, 200), + relevanceScore: analysis.relevanceScore + }], + extractedContents: [content], + analysis + }; + + console.log('URL processing complete:', { + title: content.title, + contentLength: content.content.length, + relevanceScore: analysis.relevanceScore + }); + + return result; + } catch (error) { + console.error(`Error processing URL ${url}:`, error); + return { searchResults: [] }; + } + } + + private createInitialPlan(): ResearchPlan { + return { + steps: [], + estimatedTime: 0, + maxDepth: this.options.maxDepth, + maxBranching: this.options.maxBranching, + focusAreas: [] + }; + } + + private initializeProgress(): ResearchProgress { + return { + completedSteps: 0, + totalSteps: 0, + visitedUrls: new Set(), + processedContent: 0, + startTime: new Date().toISOString() + }; + } + + private initializeFindings(): ResearchFindings { + return { + mainTopics: [], + keyInsights: [], + sources: [] + }; + } + + private async processFindings(content: ExtractedContent, analysis: ContentAnalysis, depth: number): Promise { + console.log('Processing findings for:', content.url); + + try { + // Extract code blocks and technical sections first + console.log('Extracting code blocks and technical sections...'); + const codeBlocks = this.extractCodeBlocks(content.content); + const technicalSections = this.extractTechnicalSections(content.content); + console.log('Found:', { + codeBlocks: codeBlocks.length, + technicalSections: technicalSections.length + }); + + // Update main topics with higher weight for technical content + console.log('Updating topics...'); + console.log('Before update - Topics:', this.findings.mainTopics.length); + this.updateTopics(analysis, technicalSections); + console.log('After update - Topics:', this.findings.mainTopics.length); + + // Update key insights with code examples + console.log('Updating insights...'); + console.log('Before update - Insights:', this.findings.keyInsights.length); + this.updateInsights(analysis, codeBlocks, technicalSections); + console.log('After update - Insights:', this.findings.keyInsights.length); + + // Update sources with technical content score + console.log('Updating sources...'); + console.log('Before update - Sources:', this.findings.sources.length); + this.updateSources(content, analysis, technicalSections.length > 0); + console.log('After update - Sources:', this.findings.sources.length); + + // Process related URLs if within depth limit + if (depth < this.options.maxDepth) { + console.log(`Processing related URLs at depth ${depth}...`); + await this.processRelatedUrls(content, depth + 1); + } else { + console.log(`Max depth ${this.options.maxDepth} reached, skipping related URLs`); + } + + console.log('Findings processing complete'); + } catch (error) { + console.error('Error processing findings:', error); + } + } + + private extractCodeBlocks(content: string): string[] { + const blocks: string[] = []; + // Match both fenced code blocks and inline code + const codeRegex = /```[\s\S]*?```|`[^`]+`/g; + let match; + + while ((match = codeRegex.exec(content)) !== null) { + blocks.push(match[0]); + } + + return blocks; + } + + private extractTechnicalSections(content: string): string[] { + const sections: string[] = []; + const technicalIndicators = [ + 'implementation', + 'example', + 'usage', + 'code', + 'method', + 'function', + 'class', + 'pattern', + 'practice' + ]; + + // Split content into paragraphs + const paragraphs = content.split(/\n\n+/); + + // Find paragraphs containing technical content + paragraphs.forEach(paragraph => { + const lowerParagraph = paragraph.toLowerCase(); + if ( + technicalIndicators.some(indicator => lowerParagraph.includes(indicator)) || + paragraph.includes('```') || + /`[^`]+`/.test(paragraph) + ) { + sections.push(paragraph); + } + }); + + return sections; + } + + private updateTopics(analysis: ContentAnalysis, technicalSections: string[]): void { + console.log('Updating topics with analysis:', { + topicsCount: analysis.topics ? analysis.topics.length : 0, + technicalSectionsCount: technicalSections.length + }); + + if (!analysis.topics || analysis.topics.length === 0) { + console.log('No topics found in analysis'); + return; + } + + analysis.topics.forEach(topic => { + console.log('Processing topic:', { + name: topic.name, + confidence: topic.confidence + }); + + const existingTopic = this.findings.mainTopics.find(t => t.name === topic.name); + const hasTechnicalContent = technicalSections.some(section => + section.toLowerCase().includes(topic.name.toLowerCase()) + ); + + const adjustedConfidence = hasTechnicalContent ? + Math.min(1, topic.confidence * 1.3) : + topic.confidence; + + console.log('Topic analysis:', { + hasTechnicalContent, + originalConfidence: topic.confidence, + adjustedConfidence + }); + + if (existingTopic) { + console.log('Updating existing topic:', existingTopic.name); + existingTopic.importance = Math.max(existingTopic.importance, adjustedConfidence); + } else { + console.log('Adding new topic:', topic.name); + this.findings.mainTopics.push({ + name: topic.name, + importance: adjustedConfidence, + relatedTopics: [], + evidence: [] + }); + } + }); + + // Sort topics by importance + this.findings.mainTopics.sort((a, b) => b.importance - a.importance); + console.log('Updated topics count:', this.findings.mainTopics.length); + } + + private updateInsights(analysis: ContentAnalysis, codeBlocks: string[], technicalSections: string[]): void { + analysis.keyPoints.forEach(point => { + // Find related code examples + const relatedCode = codeBlocks.filter(code => + this.isCodeRelatedToPoint(code, point.text) + ); + + // Find related technical sections + const relatedTechnical = technicalSections.filter(section => + this.isSectionRelatedToPoint(section, point.text) + ); + + // Adjust confidence based on technical content + let adjustedConfidence = point.importance; + if (relatedCode.length > 0) adjustedConfidence *= 1.2; + if (relatedTechnical.length > 0) adjustedConfidence *= 1.1; + + if (adjustedConfidence >= this.options.minRelevanceScore) { + // Convert code blocks and technical sections to Evidence objects + const evidence: Evidence[] = [ + ...relatedCode.map(code => ({ + claim: "Code example supporting the insight", + sources: [code], + confidence: 0.9 + })), + ...relatedTechnical.map(section => ({ + claim: "Technical documentation supporting the insight", + sources: [section], + confidence: 0.8 + })) + ]; + + this.findings.keyInsights.push({ + text: point.text, + confidence: Math.min(1, adjustedConfidence), + supportingEvidence: evidence, + relatedTopics: point.topics + }); + } + }); + + // Sort insights by confidence + this.findings.keyInsights.sort((a, b) => b.confidence - a.confidence); + } + + private updateSources(content: ExtractedContent, analysis: ContentAnalysis, hasTechnicalContent: boolean): void { + const source = { + url: content.url, + title: content.title, + credibilityScore: hasTechnicalContent ? + Math.min(1, analysis.quality.credibilityScore * 1.2) : + analysis.quality.credibilityScore, + contributedFindings: analysis.keyPoints.map(point => point.text) + }; + + const existingSource = this.findings.sources.find(s => s.url === content.url); + if (!existingSource) { + this.findings.sources.push(source); + } + } + + private isCodeRelatedToPoint(code: string, point: string): boolean { + const codeTerms = new Set(code.toLowerCase().split(/\W+/)); + const pointTerms = new Set(point.toLowerCase().split(/\W+/)); + + // Check for common terms + const intersection = [...pointTerms].filter(term => codeTerms.has(term)); + return intersection.length >= 2; // At least 2 common terms + } + + private isSectionRelatedToPoint(section: string, point: string): boolean { + const sectionLower = section.toLowerCase(); + const pointLower = point.toLowerCase(); + + // Check for significant term overlap + const sectionTerms = new Set(sectionLower.split(/\W+/)); + const pointTerms = new Set(pointLower.split(/\W+/)); + const intersection = [...pointTerms].filter(term => sectionTerms.has(term)); + + return intersection.length >= 3 || // At least 3 common terms + sectionLower.includes(pointLower) || // Contains the entire point + pointLower.includes(sectionLower); // Point contains the section + } + + private async processRelatedUrls(content: ExtractedContent, depth: number): Promise { + // Extract URLs from content and process them + // This would be implemented to handle actual URL extraction and processing + } + + private updateTimestamp(): void { + this.timestamp.updated = new Date().toISOString(); + } + + public async complete(): Promise { + this.status = 'completed'; + this.timestamp.completed = new Date().toISOString(); + + // Cleanup browser + if (this.context) { + await this.context.close(); + this.context = null; + } + if (this.browser) { + await this.browser.close(); + this.browser = null; + } + } +} \ No newline at end of file diff --git a/src/deep-research.ts b/src/deep-research.ts new file mode 100644 index 0000000..41f5a2f --- /dev/null +++ b/src/deep-research.ts @@ -0,0 +1,238 @@ +import { ResearchSession } from './core/research-session.js'; +import { ParallelSearch } from './parallel-search.js'; +import { SearchQueue } from './search-queue.js'; +import { SearchResult } from './types/session.js'; + +export interface DeepResearchOptions { + maxDepth?: number; + maxBranching?: number; + timeout?: number; + minRelevanceScore?: number; + maxParallelOperations?: number; +} + +export interface ResearchResult { + sessionId: string; + topic: string; + findings: { + mainTopics: Array<{ + name: string; + importance: number; + relatedTopics: string[]; + }>; + keyInsights: Array<{ + text: string; + confidence: number; + relatedTopics: string[]; + }>; + sources: Array<{ + url: string; + title: string; + credibilityScore: number; + }>; + }; + progress: { + completedSteps: number; + totalSteps: number; + processedUrls: number; + }; + timing: { + started: string; + completed?: string; + duration?: number; + operations?: { + parallelSearch?: number; + deduplication?: number; + topResultsProcessing?: number; + remainingResultsProcessing?: number; + total?: number; + }; + }; +} + +export class DeepResearch { + public parallelSearch: ParallelSearch; + private searchQueue: SearchQueue; + private activeSessions: Map; + + constructor() { + this.parallelSearch = new ParallelSearch(); + this.searchQueue = new SearchQueue(); + this.activeSessions = new Map(); + } + + private deduplicateResults(results: SearchResult[]): SearchResult[] { + const seen = new Set(); + return results.filter(result => { + const normalizedUrl = this.normalizeUrl(result.url); + if (seen.has(normalizedUrl)) { + return false; + } + seen.add(normalizedUrl); + return true; + }); + } + + private normalizeUrl(url: string): string { + try { + // Remove protocol, www, trailing slashes, and query parameters + return url + .replace(/^https?:\/\//, '') + .replace(/^www\./, '') + .replace(/\/$/, '') + .split('?')[0] + .split('#')[0] + .toLowerCase(); + } catch (error) { + return url.toLowerCase(); + } + } + + public async startResearch(topic: string, options: DeepResearchOptions = {}): Promise { + const startTime = Date.now(); + const timings: { [key: string]: number } = {}; + + console.log('[Performance] Starting research for topic:', topic); + console.log('[Performance] Options:', options); + + // Create new research session + const session = new ResearchSession(topic, { + maxDepth: options.maxDepth, + maxBranching: options.maxBranching, + timeout: options.timeout, + minRelevanceScore: options.minRelevanceScore, + maxParallelOperations: options.maxParallelOperations + }); + + console.log('[Performance] Created research session:', session.id); + this.activeSessions.set(session.id, session); + + try { + console.log('[Performance] Starting parallel search...'); + const parallelSearchStart = Date.now(); + + const queries = [ + topic, + `${topic} tutorial`, + `${topic} guide`, + `${topic} example`, + `${topic} implementation`, + `${topic} code`, + `${topic} design pattern`, + `${topic} best practice` + ]; + console.log('[Performance] Search queries:', queries); + + const searchResults = await this.parallelSearch.parallelSearch(queries); + timings.parallelSearch = Date.now() - parallelSearchStart; + console.log('[Performance] Parallel search complete. Duration:', timings.parallelSearch, 'ms'); + + const deduplicationStart = Date.now(); + const allResults = searchResults.results.flatMap(result => result.results); + console.log('[Performance] Total results:', allResults.length); + + const uniqueResults = this.deduplicateResults(allResults); + console.log('[Performance] Unique results:', uniqueResults.length); + + const sortedResults = uniqueResults.sort((a, b) => b.relevanceScore - a.relevanceScore); + timings.deduplication = Date.now() - deduplicationStart; + console.log('[Performance] Deduplication complete. Duration:', timings.deduplication, 'ms'); + + // Process top results first + console.log('[Performance] Processing top 5 results...'); + const topProcessingStart = Date.now(); + const topResults = sortedResults.slice(0, 5); + await Promise.all(topResults.map(r => { + console.log('[Performance] Processing URL:', r.url); + return session.processUrl(r.url); + })); + timings.topResultsProcessing = Date.now() - topProcessingStart; + console.log('[Performance] Top results processing complete. Duration:', timings.topResultsProcessing, 'ms'); + + // Process remaining results + console.log('[Performance] Processing remaining results...'); + const remainingProcessingStart = Date.now(); + const remainingResults = sortedResults.slice(5); + await Promise.all(remainingResults.map(r => { + console.log('[Performance] Processing URL:', r.url); + return session.processUrl(r.url); + })); + timings.remainingResultsProcessing = Date.now() - remainingProcessingStart; + console.log('[Performance] Remaining results processing complete. Duration:', timings.remainingResultsProcessing, 'ms'); + + // Complete the session + console.log('[Performance] Completing session...'); + await session.complete(); + + // Format and return results + console.log('[Performance] Formatting results...'); + const results = this.formatResults(session); + + // Add timing information + timings.total = Date.now() - startTime; + results.timing.operations = { + parallelSearch: timings.parallelSearch, + deduplication: timings.deduplication, + topResultsProcessing: timings.topResultsProcessing, + remainingResultsProcessing: timings.remainingResultsProcessing, + total: timings.total + }; + + console.log('[Performance] Research complete. Total duration:', timings.total, 'ms'); + console.log('[Performance] Operation timings:', timings); + + return results; + } catch (error) { + console.error(`[Performance] Error in research session ${session.id}:`, error); + throw error; + } finally { + // Cleanup + this.activeSessions.delete(session.id); + await this.parallelSearch.cleanup(); + } + } + + private formatResults(session: ResearchSession): ResearchResult { + return { + sessionId: session.id, + topic: session.topic, + findings: { + mainTopics: session.findings.mainTopics.map(topic => ({ + name: topic.name, + importance: topic.importance, + relatedTopics: topic.relatedTopics + })), + keyInsights: session.findings.keyInsights.map(insight => ({ + text: insight.text, + confidence: insight.confidence, + relatedTopics: insight.relatedTopics + })), + sources: session.findings.sources.map(source => ({ + url: source.url, + title: source.title, + credibilityScore: source.credibilityScore + })) + }, + progress: { + completedSteps: session.progress.completedSteps, + totalSteps: session.progress.totalSteps, + processedUrls: session.progress.visitedUrls.size + }, + timing: { + started: session.timestamp.created, + completed: session.timestamp.completed, + duration: session.timestamp.completed ? + new Date(session.timestamp.completed).getTime() - new Date(session.timestamp.created).getTime() + : undefined + } + }; + } + + public async getSessionStatus(sessionId: string): Promise { + const session = this.activeSessions.get(sessionId); + if (!session) return null; + return this.formatResults(session); + } +} + +export default DeepResearch; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..1cee7e3 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,409 @@ +#!/usr/bin/env node +import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { + CallToolRequestSchema, + ErrorCode, + ListToolsRequestSchema, + McpError +} from '@modelcontextprotocol/sdk/types.js'; +import { chromium, Browser, Page } from 'playwright'; +import TurndownService from 'turndown'; + +import DeepResearch from './deep-research.js'; + +interface DeepResearchArgs { + topic: string; + maxDepth?: number; + maxBranching?: number; + timeout?: number; + minRelevanceScore?: number; +} + +interface ParallelSearchArgs { + queries: string[]; + maxParallel?: number; +} + +interface VisitPageArgs { + url: string; +} + +// Initialize Turndown service for converting HTML to Markdown +const turndownService = new TurndownService({ + headingStyle: 'atx', + hr: '---', + bulletListMarker: '-', + codeBlockStyle: 'fenced', + emDelimiter: '_', + strongDelimiter: '**', + linkStyle: 'inlined', +}); + +// Custom Turndown rules +turndownService.addRule('removeScripts', { + filter: ['script', 'style', 'noscript'], + replacement: () => '' +}); + +turndownService.addRule('preserveLinks', { + filter: 'a', + replacement: (content: string, node: Node) => { + const element = node as HTMLAnchorElement; + const href = element.getAttribute('href'); + return href ? `[${content}](${href})` : content; + } +}); + +// Redirect console output to stderr to keep stdout clean for MCP communication +const originalConsoleLog = console.log; +const originalConsoleError = console.error; +console.log = (...args) => { + process.stderr.write(`[INFO] ${args.join(' ')}\n`); +}; +console.error = (...args) => { + process.stderr.write(`[ERROR] ${args.join(' ')}\n`); +}; + +const deepResearch = new DeepResearch(); +let browser: Browser | undefined; +let page: Page | undefined; + +const server = new Server( + { + name: 'mcp-deepwebresearch', + version: '0.3.0' + }, + { + capabilities: { + tools: {} + } + } +); + +// List available tools +server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: 'deep_research', + description: 'Perform deep research on a topic with content extraction and analysis', + inputSchema: { + type: 'object', + properties: { + topic: { + type: 'string', + description: 'Research topic or question' + }, + maxDepth: { + type: 'number', + description: 'Maximum depth of related content exploration', + minimum: 1, + maximum: 2 + }, + maxBranching: { + type: 'number', + description: 'Maximum number of related paths to explore', + minimum: 1, + maximum: 3 + }, + timeout: { + type: 'number', + description: 'Research timeout in milliseconds', + minimum: 30000, + maximum: 55000 + }, + minRelevanceScore: { + type: 'number', + description: 'Minimum relevance score for including content', + minimum: 0, + maximum: 1 + } + }, + required: ['topic'] + } + }, + { + name: 'parallel_search', + description: 'Perform multiple Google searches in parallel', + inputSchema: { + type: 'object', + properties: { + queries: { + type: 'array', + items: { + type: 'string' + }, + description: 'Array of search queries to execute in parallel' + }, + maxParallel: { + type: 'number', + description: 'Maximum number of parallel searches', + minimum: 1, + maximum: 5 + } + }, + required: ['queries'] + } + }, + { + name: 'visit_page', + description: 'Visit a webpage and extract its content', + inputSchema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'URL to visit' + } + }, + required: ['url'] + } + } + ] +})); + +// Validate URL format and security +function isValidUrl(urlString: string): boolean { + try { + const url = new URL(urlString); + return url.protocol === 'http:' || url.protocol === 'https:'; + } catch { + return false; + } +} + +// Safe page navigation with timeout +async function safePageNavigation(page: Page, url: string): Promise { + await page.goto(url, { + waitUntil: 'domcontentloaded', + timeout: 10000 // 10 second timeout + }); + + // Quick check for bot protection or security challenges + const validation = await page.evaluate(() => { + const botProtectionExists = [ + '#challenge-running', + '#cf-challenge-running', + '#px-captcha', + '#ddos-protection', + '#waf-challenge-html' + ].some(selector => document.querySelector(selector)); + + const suspiciousTitle = [ + 'security check', + 'ddos protection', + 'please wait', + 'just a moment', + 'attention required' + ].some(phrase => document.title.toLowerCase().includes(phrase)); + + return { + botProtection: botProtectionExists, + suspiciousTitle, + title: document.title + }; + }); + + if (validation.botProtection) { + throw new Error('Bot protection detected'); + } + + if (validation.suspiciousTitle) { + throw new Error(`Suspicious page title detected: "${validation.title}"`); + } +} + +// Extract content as markdown +async function extractContentAsMarkdown(page: Page): Promise { + const html = await page.evaluate(() => { + // Try standard content containers first + const contentSelectors = [ + 'main', + 'article', + '[role="main"]', + '#content', + '.content', + '.main', + '.post', + '.article' + ]; + + for (const selector of contentSelectors) { + const element = document.querySelector(selector); + if (element) { + return element.outerHTML; + } + } + + // Fallback to cleaning full body content + const body = document.body; + const elementsToRemove = [ + 'header', 'footer', 'nav', + '[role="navigation"]', 'aside', + '.sidebar', '[role="complementary"]', + '.nav', '.menu', '.header', + '.footer', '.advertisement', + '.ads', '.cookie-notice' + ]; + + elementsToRemove.forEach(sel => { + body.querySelectorAll(sel).forEach(el => el.remove()); + }); + + return body.outerHTML; + }); + + if (!html) { + return ''; + } + + try { + const markdown = turndownService.turndown(html); + return markdown + .replace(/\n{3,}/g, '\n\n') + .replace(/^- $/gm, '') + .replace(/^\s+$/gm, '') + .trim(); + } catch (error) { + console.error('Error converting HTML to Markdown:', error); + return html; + } +} + +// Ensure browser is initialized +async function ensureBrowser(): Promise { + if (!browser) { + browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + page = await context.newPage(); + } + + if (!page) { + const context = await browser.newContext(); + page = await context.newPage(); + } + + return page; +} + +// Handle tool calls +server.setRequestHandler(CallToolRequestSchema, async (request) => { + try { + switch (request.params.name) { + case 'deep_research': { + const args = request.params.arguments as unknown as DeepResearchArgs; + if (!args?.topic) { + throw new McpError(ErrorCode.InvalidParams, 'Topic is required'); + } + + console.log(`Starting deep research on topic: ${args.topic}`); + const result = await deepResearch.startResearch(args.topic, { + maxDepth: Math.min(args.maxDepth || 2, 2), + maxBranching: Math.min(args.maxBranching || 3, 3), + timeout: Math.min(args.timeout || 55000, 55000), + minRelevanceScore: args.minRelevanceScore || 0.7 + }); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result, null, 2) + } + ] + }; + } + + case 'parallel_search': { + const args = request.params.arguments as unknown as ParallelSearchArgs; + if (!args?.queries) { + throw new McpError(ErrorCode.InvalidParams, 'Queries array is required'); + } + + const limitedQueries = args.queries.slice(0, 5); + console.log(`Starting parallel search with ${limitedQueries.length} queries`); + const result = await deepResearch.parallelSearch.parallelSearch(limitedQueries); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result, null, 2) + } + ] + }; + } + + case 'visit_page': { + const args = request.params.arguments as unknown as VisitPageArgs; + if (!args?.url) { + throw new McpError(ErrorCode.InvalidParams, 'URL is required'); + } + + if (!isValidUrl(args.url)) { + throw new McpError( + ErrorCode.InvalidParams, + `Invalid URL: ${args.url}. Only http and https protocols are supported.` + ); + } + + const page = await ensureBrowser(); + try { + await safePageNavigation(page, args.url); + const title = await page.title(); + const content = await extractContentAsMarkdown(page); + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + url: args.url, + title, + content + }, null, 2) + } + ] + }; + } catch (error) { + throw new McpError( + ErrorCode.InternalError, + `Failed to visit page: ${(error as Error).message}` + ); + } + } + + default: + throw new McpError( + ErrorCode.MethodNotFound, + `Unknown tool: ${request.params.name}` + ); + } + } catch (error) { + console.error('Error executing tool:', error); + throw new McpError( + ErrorCode.InternalError, + error instanceof Error ? error.message : 'Unknown error occurred' + ); + } +}); + +// Error handling +server.onerror = (error) => { + console.error('[MCP Error]', error); +}; + +// Handle shutdown +process.on('SIGINT', async () => { + if (browser) { + await browser.close(); + } + await server.close(); + process.exit(0); +}); + +// Start the server +const transport = new StdioServerTransport(); +server.connect(transport).catch(console.error); + +console.error('MCP Web Research server running on stdio'); \ No newline at end of file diff --git a/src/parallel-search.ts b/src/parallel-search.ts new file mode 100644 index 0000000..0235c07 --- /dev/null +++ b/src/parallel-search.ts @@ -0,0 +1,278 @@ +import { Browser, BrowserContext, chromium } from 'playwright'; +import { writeFile, mkdir } from 'fs/promises'; +import path from 'path'; +import os from 'os'; +import { ParallelSearchResult, SearchResult, SearchOptions } from './types.js'; + +const USER_AGENTS = [ + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15', + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0' +]; + +const VIEWPORT_SIZES = [ + { width: 1920, height: 1080 }, + { width: 1366, height: 768 }, + { width: 1536, height: 864 }, + { width: 1440, height: 900 }, + { width: 1280, height: 720 } +]; + +export class ParallelSearch { + private browser: Browser | null = null; + private contexts: BrowserContext[] = []; + private options: Required; + + constructor(options: SearchOptions = {}) { + this.options = { + maxParallel: options.maxParallel || 10, + delayBetweenSearches: options.delayBetweenSearches || 200, + outputDir: path.isAbsolute(options.outputDir || '') + ? (options.outputDir || path.join(os.tmpdir(), 'search-results')) + : path.join(os.tmpdir(), options.outputDir || 'search-results'), + retryAttempts: options.retryAttempts || 3, + includeTimings: options.includeTimings || false + }; + } + + private getSearchResult(result: SearchResult[], searchId: string, query: string, startTime?: number, error?: string): ParallelSearchResult { + const base: ParallelSearchResult = { + searchId, + query, + results: result, + error + }; + + if (this.options.includeTimings && startTime) { + return { + ...base, + executionTime: Date.now() - startTime + }; + } + + return base; + } + + private async initialize(): Promise { + if (!this.browser) { + this.browser = await chromium.launch({ headless: true }); + // Create browser contexts + for (let i = 0; i < this.options.maxParallel; i++) { + const context = await this.browser.newContext({ + userAgent: USER_AGENTS[i % USER_AGENTS.length], + viewport: VIEWPORT_SIZES[i % VIEWPORT_SIZES.length], + deviceScaleFactor: 1 + (Math.random() * 0.5), + hasTouch: Math.random() > 0.5 + }); + this.contexts.push(context); + } + } + } + + private async saveResults(searchId: string, query: string, results: SearchResult[]): Promise { + const filename = `${searchId}-${query.replace(/[^a-z0-9]/gi, '_')}.json`; + const outputDir = this.options.outputDir; + + // Create output directory if it doesn't exist + await mkdir(outputDir, { recursive: true }); + + const filepath = path.join(outputDir, filename); + await writeFile(filepath, JSON.stringify({ + searchId, + query, + timestamp: new Date().toISOString(), + results + }, null, 2)); + return filepath; + } + + private async singleSearch( + context: BrowserContext, + query: string, + searchId: string + ): Promise { + const startTime = this.options.includeTimings ? Date.now() : undefined; + const page = await context.newPage(); + try { + await page.goto('https://www.google.com', { waitUntil: 'networkidle' }); + + // Wait for and handle any consent dialog + try { + const consentButton = await page.$('button:has-text("Accept all")'); + if (consentButton) { + await consentButton.click(); + await page.waitForLoadState('networkidle'); + } + } catch (error) { + // Ignore consent handling errors + } + + // Try different selectors for search input + const searchInput = await page.$( + 'textarea[name="q"], input[name="q"], input[type="text"]' + ); + + if (!searchInput) { + throw new Error('Search input not found'); + } + + await searchInput.click(); + await searchInput.fill(query); + await Promise.all([ + page.keyboard.press('Enter'), + page.waitForNavigation({ waitUntil: 'networkidle' }) + ]); + + // Wait for search results to appear + await page.waitForSelector('div.g', { timeout: 10000 }); + + // Extract results after ensuring they're loaded + const results = await page.$$eval('div.g', (elements, query) => { + return elements.map((el, index) => { + const titleEl = el.querySelector('h3'); + const linkEl = el.querySelector('a'); + const snippetEl = el.querySelector('div.VwiC3b'); + + if (!titleEl || !linkEl || !snippetEl) return null; + + const title = titleEl.textContent || ''; + const url = linkEl.href || ''; + const snippet = snippetEl.textContent || ''; + + // Calculate relevance score based on multiple factors + let relevanceScore = 0; + + // Position score (earlier results are more relevant) + relevanceScore += Math.max(0, 1 - (index * 0.1)); + + // Title match score + const titleMatchScore = title.toLowerCase().includes(query.toLowerCase()) ? 0.3 : 0; + relevanceScore += titleMatchScore; + + // Snippet match score + const snippetMatchScore = snippet.toLowerCase().includes(query.toLowerCase()) ? 0.2 : 0; + relevanceScore += snippetMatchScore; + + // URL quality score + const urlQualityScore = + url.includes('.edu') ? 0.3 : + url.includes('.gov') ? 0.3 : + url.includes('github.com') ? 0.25 : + url.includes('stackoverflow.com') ? 0.25 : + url.includes('docs.') ? 0.25 : + 0.1; + relevanceScore += urlQualityScore; + + return { + title, + url, + snippet, + relevanceScore: Math.min(1, relevanceScore) + }; + }).filter(result => result !== null); + }, query); + + if (!results || results.length === 0) { + throw new Error('No search results found'); + } + + await this.saveResults(searchId, query, results); + return this.getSearchResult(results, searchId, query, startTime); + } catch (error) { + return this.getSearchResult( + [], + searchId, + query, + startTime, + error instanceof Error ? error.message : 'Unknown error occurred' + ); + } finally { + await page.close(); + } + } + + public async parallelSearch(queries: string[]): Promise<{ + results: ParallelSearchResult[]; + summary: { + totalQueries: number; + successful: number; + failed: number; + totalExecutionTime?: number; + averageExecutionTime?: number; + }; + }> { + const startTime = this.options.includeTimings ? Date.now() : undefined; + await this.initialize(); + + const results: ParallelSearchResult[] = []; + const chunks: string[][] = []; + + // Split queries into chunks of maxParallel size + for (let i = 0; i < queries.length; i += this.options.maxParallel) { + chunks.push(queries.slice(i, i + this.options.maxParallel)); + } + + // Process each chunk + for (const chunk of chunks) { + const chunkPromises = chunk.map((query, index) => { + const searchId = `search_${Date.now()}_${index + 1}_of_${chunk.length}`; + // Stagger the searches + return new Promise(async (resolve) => { + await new Promise(r => setTimeout(r, index * this.options.delayBetweenSearches)); + const result = await this.singleSearch( + this.contexts[index % this.contexts.length], + query, + searchId + ); + resolve(result); + }); + }); + + const chunkResults = await Promise.all(chunkPromises); + results.push(...chunkResults); + + // Add a small delay between chunks + if (chunks.indexOf(chunk) < chunks.length - 1) { + await new Promise(r => setTimeout(r, 1000)); + } + } + + const endTime = Date.now(); + const successful = results.filter(r => !r.error).length; + const failed = results.filter(r => r.error).length; + + const summary = { + totalQueries: queries.length, + successful, + failed, + ...(this.options.includeTimings && startTime ? { + totalExecutionTime: endTime - startTime, + averageExecutionTime: Math.round((endTime - startTime) / queries.length) + } : {}) + }; + + // Add individual execution times to results if timing is enabled + const timedResults = this.options.includeTimings ? results.map(r => ({ + ...r, + executionTime: r.executionTime || 0 + })) : results; + + return { + results: timedResults, + summary + }; + } + + public async cleanup(): Promise { + for (const context of this.contexts) { + await context.close(); + } + this.contexts = []; + if (this.browser) { + await this.browser.close(); + this.browser = null; + } + } +} \ No newline at end of file diff --git a/src/search-queue.ts b/src/search-queue.ts new file mode 100644 index 0000000..802ca19 --- /dev/null +++ b/src/search-queue.ts @@ -0,0 +1,138 @@ +import { RateLimiterMemory } from 'rate-limiter-flexible'; +import EventEmitter from 'events'; + +interface SearchQueueItem { + id: string; + query: string; + status: 'pending' | 'in_progress' | 'completed' | 'failed'; + results?: any[]; + error?: string; + timestamp: number; + retryCount: number; +} + +interface QueueStatus { + totalItems: number; + completed: number; + pending: number; + failed: number; + currentItem?: SearchQueueItem; +} + +export class SearchQueue extends EventEmitter { + private queue: SearchQueueItem[] = []; + private inProgress: boolean = false; + private rateLimiter: RateLimiterMemory; + + constructor() { + super(); + // Allow 1 request per 2 seconds with burst of 3 + this.rateLimiter = new RateLimiterMemory({ + points: 3, + duration: 6, + }); + } + + public async addSearch(query: string): Promise { + const id = `search_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + const item: SearchQueueItem = { + id, + query, + status: 'pending', + timestamp: Date.now(), + retryCount: 0 + }; + + this.queue.push(item); + this.emit('itemAdded', item); + + if (!this.inProgress) { + this.processQueue(); + } + + return id; + } + + public async addBatchSearch(queries: string[]): Promise { + return Promise.all(queries.map(query => this.addSearch(query))); + } + + public getStatus(): QueueStatus { + const completed = this.queue.filter(item => item.status === 'completed').length; + const pending = this.queue.filter(item => item.status === 'pending').length; + const failed = this.queue.filter(item => item.status === 'failed').length; + const currentItem = this.queue.find(item => item.status === 'in_progress'); + + return { + totalItems: this.queue.length, + completed, + pending, + failed, + currentItem + }; + } + + public cancelSearch(id: string): boolean { + const index = this.queue.findIndex(item => item.id === id && item.status === 'pending'); + if (index !== -1) { + this.queue[index].status = 'failed'; + this.queue[index].error = 'Cancelled by user'; + this.emit('itemCancelled', this.queue[index]); + return true; + } + return false; + } + + private async processQueue(): Promise { + if (this.inProgress || this.queue.length === 0) { + return; + } + + this.inProgress = true; + + while (this.queue.some(item => item.status === 'pending')) { + try { + await this.rateLimiter.consume('search', 1); + + const item = this.queue.find(item => item.status === 'pending'); + if (!item) continue; + + item.status = 'in_progress'; + this.emit('itemStarted', item); + + try { + // Perform the search - this will be implemented in the browser class + // const results = await this.browser.search(item.query); + // item.results = results; + item.status = 'completed'; + this.emit('itemCompleted', item); + } catch (error) { + if (item.retryCount < 3) { + item.retryCount++; + item.status = 'pending'; + this.emit('itemRetrying', item); + // Add exponential backoff delay + await new Promise(resolve => setTimeout(resolve, Math.pow(2, item.retryCount) * 1000)); + } else { + item.status = 'failed'; + item.error = error instanceof Error ? error.message : 'Unknown error occurred'; + this.emit('itemFailed', item); + } + } + } catch (error) { + // Rate limiter error - wait and try again + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } + + this.inProgress = false; + this.emit('queueCompleted', this.getStatus()); + } + + public clearCompleted(): void { + this.queue = this.queue.filter(item => + item.status !== 'completed' && item.status !== 'failed' + ); + this.emit('queueUpdated', this.getStatus()); + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..cdbf0a1 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,37 @@ +export interface SearchResult { + title: string; + url: string; + snippet: string; + relevanceScore: number; +} + +export interface ParallelSearchResult { + searchId: string; + query: string; + results: SearchResult[]; + error?: string; + executionTime?: number; +} + +export interface SearchOptions { + maxParallel?: number; + delayBetweenSearches?: number; + outputDir?: string; + retryAttempts?: number; + includeTimings?: boolean; +} + +export interface SearchSummary { + totalQueries: number; + successful: number; + failed: number; + totalExecutionTime?: number; + averageExecutionTime?: number; +} + +export interface SearchOptions { + maxParallel?: number; + delayBetweenSearches?: number; + outputDir?: string; + retryAttempts?: number; +} \ No newline at end of file diff --git a/src/types/analysis.ts b/src/types/analysis.ts new file mode 100644 index 0000000..387aa5f --- /dev/null +++ b/src/types/analysis.ts @@ -0,0 +1,80 @@ +export interface Topic { + name: string; + confidence: number; + keywords: string[]; +} + +export interface KeyPoint { + text: string; + importance: number; + topics: string[]; + supportingEvidence: string[]; +} + +export type EntityType = 'standard' | 'algorithm' | 'organization' | 'person' | 'technology'; + +export interface EntityMention { + text: string; + position: { + start: number; + end: number; + }; + context: string; +} + +export interface Entity { + name: string; + type: EntityType; + mentions: EntityMention[]; +} + +export interface Relationship { + source: string; + target: string; + type: string; + confidence: number; +} + +export interface Citation { + text: string; + type: 'standard' | 'url' | 'reference'; + source?: string; +} + +export interface SentimentAnalysis { + score: number; + confidence: number; + aspects: Array<{ + aspect: string; + score: number; + }>; +} + +export interface ContentQuality { + readability: number; + informationDensity: number; + technicalDepth: number; + credibilityScore: number; + freshness: number; +} + +export interface ContentAnalysis { + relevanceScore: number; + topics: Topic[]; + keyPoints: KeyPoint[]; + entities: Entity[]; + sentiment: SentimentAnalysis; + relationships: Relationship[]; + citations: Citation[]; + quality: ContentQuality; +} + +export interface AnalysisOptions { + maxTopics?: number; + maxKeyPoints?: number; + minConfidence?: number; + minImportance?: number; + includeSentiment?: boolean; + includeRelationships?: boolean; + includeCitations?: boolean; +} \ No newline at end of file diff --git a/src/types/content.ts b/src/types/content.ts new file mode 100644 index 0000000..2912536 --- /dev/null +++ b/src/types/content.ts @@ -0,0 +1,58 @@ +export interface ExtractedContent { + url: string; + title: string; + content: string; + html?: string; + timestamp: string; + metadata: ContentMetadata; + structuredData?: any[]; +} + +export interface ContentMetadata { + author?: string; + datePublished?: string; + lastModified?: string; + language?: string; + readingTime?: number; + wordCount?: number; +} + +export interface ContentSection { + id: string; + title?: string; + content: string; + importance: number; + type: 'main' | 'technical' | 'sidebar' | 'header' | 'footer' | 'navigation' | 'other'; +} + +export interface StructuredContent { + mainContent: ContentSection[]; + relatedLinks: string[]; + images: ImageContent[]; + tables: TableContent[]; +} + +export interface ImageContent { + url: string; + alt?: string; + caption?: string; + dimensions?: { + width: number; + height: number; + }; +} + +export interface TableContent { + headers: string[]; + rows: string[][]; + caption?: string; +} + +export interface ContentExtractionOptions { + includeHtml?: boolean; + extractStructuredData?: boolean; + extractImages?: boolean; + extractTables?: boolean; + maxContentLength?: number; + timeout?: number; +} \ No newline at end of file diff --git a/src/types/session.ts b/src/types/session.ts new file mode 100644 index 0000000..4b6b828 --- /dev/null +++ b/src/types/session.ts @@ -0,0 +1,162 @@ +import { ExtractedContent } from './content'; +import { ContentAnalysis } from './analysis'; + +export interface ResearchSession { + id: string; + topic: string; + status: ResearchStatus; + plan: ResearchPlan; + progress: ResearchProgress; + findings: ResearchFindings; + timestamp: { + created: string; + updated: string; + completed?: string; + }; +} + +export type ResearchStatus = + | 'planning' + | 'in_progress' + | 'analyzing' + | 'synthesizing' + | 'completed' + | 'failed' + | 'cancelled'; + +export interface ResearchPlan { + steps: ResearchStep[]; + estimatedTime: number; + maxDepth: number; + maxBranching: number; + focusAreas: string[]; +} + +export interface ResearchStep { + id: string; + type: StepType; + status: StepStatus; + query: string; + dependsOn: string[]; + refinements: string[]; + results: StepResult; + timing: { + started?: string; + completed?: string; + duration?: number; + }; +} + +export type StepType = + | 'initial_search' + | 'follow_up_search' + | 'content_extraction' + | 'analysis' + | 'synthesis'; + +export type StepStatus = + | 'pending' + | 'in_progress' + | 'completed' + | 'failed' + | 'skipped'; + +export interface StepResult { + searchResults?: SearchResult[]; + extractedContents?: ExtractedContent[]; + analysis?: ContentAnalysis; + synthesis?: SynthesisResult; +} + +export interface SearchResult { + url: string; + title: string; + snippet: string; + relevanceScore: number; +} + +export interface SynthesisResult { + summary: string; + keyFindings: string[]; + relationships: RelationshipMap; + evidence: Evidence[]; +} + +export interface RelationshipMap { + nodes: Node[]; + edges: Edge[]; +} + +export interface Node { + id: string; + type: string; + label: string; + properties: Record; +} + +export interface Edge { + source: string; + target: string; + type: string; + properties: Record; +} + +export interface Evidence { + claim: string; + sources: string[]; + confidence: number; +} + +export interface ResearchProgress { + completedSteps: number; + totalSteps: number; + currentStep?: string; + visitedUrls: Set; + processedContent: number; + startTime: string; + estimatedCompletion?: string; +} + +export interface ResearchFindings { + mainTopics: Topic[]; + keyInsights: KeyInsight[]; + timeline?: TimelineEvent[]; + sources: Source[]; +} + +export interface Topic { + name: string; + importance: number; + relatedTopics: string[]; + evidence: Evidence[]; +} + +export interface KeyInsight { + text: string; + confidence: number; + supportingEvidence: Evidence[]; + relatedTopics: string[]; +} + +export interface TimelineEvent { + date: string; + description: string; + importance: number; + sources: string[]; +} + +export interface Source { + url: string; + title: string; + credibilityScore: number; + contributedFindings: string[]; +} + +export interface SessionOptions { + maxSteps?: number; + maxDepth?: number; + maxBranching?: number; + timeout?: number; + minRelevanceScore?: number; + maxParallelOperations?: number; +} \ No newline at end of file diff --git a/summarize_report.py b/summarize_report.py new file mode 100644 index 0000000..9bbbfe5 --- /dev/null +++ b/summarize_report.py @@ -0,0 +1,163 @@ +import os +import json +from typing import List, Dict +import boto3 +from dotenv import load_dotenv +from concurrent.futures import ThreadPoolExecutor, as_completed + +# Load environment variables +load_dotenv() + +class BedrockSummarizer: + def __init__(self): + self.bedrock = boto3.client( + service_name='bedrock-runtime', + region_name=os.getenv('AWS_REGION'), + aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), + aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY') + ) + self.model_id = "anthropic.claude-3-5-sonnet-20241022-v2:0" + self.max_tokens = 4000 + self.temperature = 0.7 + + def create_prompt(self, content: str, task: str, is_final: bool = False) -> dict: + if is_final: + prompt = f"""Please create a cohesive final summary about AWS Bedrock focusing on {task}. + Use these previous chunk summaries to create a well-organized, comprehensive analysis: + + {content} + + Provide a detailed yet concise summary that brings together the key points from all chunks into a unified analysis.""" + else: + prompt = f"""Please analyze and summarize this portion of AWS Bedrock documentation. Focus on {task}: + + {content} + + Provide a detailed analysis focusing specifically on the requested aspect. Be thorough but concise, and include specific details about features, capabilities, and requirements where relevant.""" + + return { + "anthropic_version": "bedrock-2023-05-31", + "max_tokens": self.max_tokens, + "messages": [ + { + "role": "user", + "content": prompt + } + ], + "temperature": self.temperature + } + + def invoke_model(self, prompt: dict) -> str: + try: + response = self.bedrock.invoke_model( + modelId=self.model_id, + body=json.dumps(prompt) + ) + response_body = json.loads(response.get('body').read()) + return response_body['content'][0]['text'] + except Exception as e: + print(f"Error invoking model: {str(e)}") + return "" + + def process_chunk(self, chunk: str, task: str) -> str: + prompt = self.create_prompt(chunk, task) + return self.invoke_model(prompt) + + def create_final_summary(self, chunk_summaries: List[str], task: str) -> str: + combined_summaries = "\n\n".join(chunk_summaries) + prompt = self.create_prompt(combined_summaries, task, is_final=True) + return self.invoke_model(prompt) + + def process_content(self, chunks: List[str]) -> Dict[str, str]: + # Define analysis tasks + tasks = { + "overview": "main features and capabilities of Amazon Bedrock", + "models": "supported foundation models and their characteristics", + "setup": "setup and configuration requirements", + "features": "key features like model inference, knowledge bases, and agents", + "security": "security considerations and best practices" + } + + results = {} + for task_name, task_desc in tasks.items(): + print(f"\nProcessing task: {task_name}") + chunk_summaries = [] + + # Process each chunk + for i, chunk in enumerate(chunks): + print(f"Processing chunk {i+1}/{len(chunks)} for {task_name}...") + summary = self.process_chunk(chunk, task_desc) + if summary: + chunk_summaries.append(summary) + + # Create final summary for this task + if chunk_summaries: + print(f"Creating final summary for {task_name}...") + results[task_name] = self.create_final_summary(chunk_summaries, task_desc) + else: + results[task_name] = f"Error: No valid summaries generated for {task_name}" + + return results + +def main(): + # Check for required environment variables + required_env_vars = ['AWS_REGION', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'] + missing_vars = [var for var in required_env_vars if not os.getenv(var)] + if missing_vars: + print("Error: Missing required environment variables:", missing_vars) + print("Please set these variables in your .env file") + return + + # Initialize summarizer + summarizer = BedrockSummarizer() + + # Load chunks from JSON file + print("Loading document chunks...") + try: + with open('chunks.json', 'r', encoding='utf-8') as f: + chunks_data = json.load(f) + chunks = chunks_data.get('chunks', []) + print(f"Loaded {len(chunks)} chunks") + except FileNotFoundError: + print("Error: chunks.json not found. Please run chunk_document.py first and save the chunks.") + return + except json.JSONDecodeError: + print("Error: Invalid JSON format in chunks.json") + return + + # Process chunks + results = summarizer.process_content(chunks) + + # Generate final report + final_report = """# Amazon Bedrock Documentation Analysis +Generated using AWS Bedrock Claude 3.5 Sonnet + +## 1. Overview and Key Features +{} + +## 2. Foundation Models +{} + +## 3. Setup and Configuration +{} + +## 4. Key Features and Capabilities +{} + +## 5. Security and Best Practices +{} +""".format( + results['overview'], + results['models'], + results['setup'], + results['features'], + results['security'] + ) + + # Write final report + with open('TOPIC-FINAL-REPORT.txt', 'w') as f: + f.write(final_report) + print("\nFinal report has been generated: TOPIC-FINAL-REPORT.txt") + +if __name__ == "__main__": + main() diff --git a/tsconfig.json b/tsconfig.json index c7c0fc0..1704786 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,25 +1,35 @@ { "compilerOptions": { - "target": "ES2023", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "lib": ["ES2020", "DOM"], + "outDir": "./dist", + "rootDir": "./src", "strict": true, - "outDir": "dist", - "sourceMap": true, - "declaration": true, + "esModuleInterop": true, "skipLibCheck": true, - "lib": [ - "ES2023", - "DOM", - "DOM.Iterable" - ] + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "sourceMap": true, + "allowJs": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true }, "include": [ - "*.ts" + "src/**/*" ], "exclude": [ "node_modules", - "dist" + "dist", + "**/*.test.ts" ] } \ No newline at end of file