diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..bb47b47
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,35 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG]"
+labels: bug
+assignees: ''
+
+---
+
+**Describe the Bug**
+Provide a clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the issue:
+1. Configure the environment or settings with '...'
+2. Run the command '...'
+3. Observe the error or unexpected output at '...'
+4. Log output/error message
+
+**Expected Behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots or copies of the command line output to help explain the issue.
+
+**Environment (please complete the following information):**
+- OS: [e.g. macOS, Linux, Windows]
+- Firecrawl Version: [e.g. 1.2.3]
+- Node.js Version: [e.g. 14.x]
+
+**Logs**
+If applicable, include detailed logs to help understand the problem.
+
+**Additional Context**
+Add any other context about the problem here, such as configuration specifics, network conditions, data volumes, etc.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..b01699b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,26 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[Feat]"
+labels: ''
+assignees: ''
+
+---
+
+**Problem Description**
+Describe the issue you're experiencing that has prompted this feature request. For example, "I find it difficult when..."
+
+**Proposed Feature**
+Provide a clear and concise description of the feature you would like implemented.
+
+**Alternatives Considered**
+Discuss any alternative solutions or features you've considered. Why were these alternatives not suitable?
+
+**Implementation Suggestions**
+If you have ideas on how the feature could be implemented, share them here. This could include technical details, API changes, or interaction mechanisms.
+
+**Use Case**
+Explain how this feature would be used and what benefits it would bring. Include specific examples to illustrate how this would improve functionality or user experience.
+
+**Additional Context**
+Add any other context such as comparisons with similar features in other products, or links to prototypes or mockups.
diff --git a/.github/archive/js-sdk.yml b/.github/archive/js-sdk.yml
new file mode 100644
index 0000000..c84bb8b
--- /dev/null
+++ b/.github/archive/js-sdk.yml
@@ -0,0 +1,58 @@
+name: Run JavaScript SDK E2E Tests
+
+on: []
+
+env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ BULL_AUTH_KEY: ${{ secrets.BULL_AUTH_KEY }}
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ HOST: ${{ secrets.HOST }}
+ LLAMAPARSE_API_KEY: ${{ secrets.LLAMAPARSE_API_KEY }}
+ LOGTAIL_KEY: ${{ secrets.LOGTAIL_KEY }}
+ POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
+ POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
+ NUM_WORKERS_PER_QUEUE: ${{ secrets.NUM_WORKERS_PER_QUEUE }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ PLAYWRIGHT_MICROSERVICE_URL: ${{ secrets.PLAYWRIGHT_MICROSERVICE_URL }}
+ PORT: ${{ secrets.PORT }}
+ REDIS_URL: ${{ secrets.REDIS_URL }}
+ SCRAPING_BEE_API_KEY: ${{ secrets.SCRAPING_BEE_API_KEY }}
+ SUPABASE_ANON_TOKEN: ${{ secrets.SUPABASE_ANON_TOKEN }}
+ SUPABASE_SERVICE_TOKEN: ${{ secrets.SUPABASE_SERVICE_TOKEN }}
+ SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
+ TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
+ HYPERDX_API_KEY: ${{ secrets.HYPERDX_API_KEY }}
+ HDX_NODE_BETA_MODE: 1
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ services:
+ redis:
+ image: redis
+ ports:
+ - 6379:6379
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: "20"
+ - name: Install pnpm
+ run: npm install -g pnpm
+ - name: Install dependencies for API
+ run: pnpm install
+ working-directory: ./apps/api
+ - name: Start the application
+ run: npm start &
+ working-directory: ./apps/api
+ - name: Start workers
+ run: npm run workers &
+ working-directory: ./apps/api
+ - name: Install dependencies for JavaScript SDK
+ run: pnpm install
+ working-directory: ./apps/js-sdk/firecrawl
+ - name: Run E2E tests for JavaScript SDK
+ run: npm run test
+ working-directory: ./apps/js-sdk/firecrawl
\ No newline at end of file
diff --git a/.github/archive/publish-js-sdk.yml b/.github/archive/publish-js-sdk.yml
new file mode 100644
index 0000000..c02a654
--- /dev/null
+++ b/.github/archive/publish-js-sdk.yml
@@ -0,0 +1,46 @@
+name: Publish JavaScript SDK
+
+on: []
+
+env:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+jobs:
+ build-and-publish:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '20'
+ registry-url: 'https://registry.npmjs.org/'
+ scope: '@mendable'
+ always-auth: true
+
+ - name: Install pnpm
+ run: npm install -g pnpm
+
+ - name: Install python for running version check script
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel requests packaging
+
+ - name: Install dependencies for JavaScript SDK
+ run: pnpm install
+ working-directory: ./apps/js-sdk/firecrawl
+
+ - name: Run version check script
+ id: version_check_script
+ run: |
+ VERSION_INCREMENTED=$(python .github/scripts/check_version_has_incremented.py js ./apps/js-sdk/firecrawl @mendable/firecrawl-js)
+ echo "VERSION_INCREMENTED=$VERSION_INCREMENTED" >> $GITHUB_ENV
+
+ - name: Build and publish to npm
+ if: ${{ env.VERSION_INCREMENTED == 'true' }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: |
+ npm run build-and-publish
+ working-directory: ./apps/js-sdk/firecrawl
diff --git a/.github/archive/publish-python-sdk.yml b/.github/archive/publish-python-sdk.yml
new file mode 100644
index 0000000..6d86f1e
--- /dev/null
+++ b/.github/archive/publish-python-sdk.yml
@@ -0,0 +1,47 @@
+name: Publish Python SDK
+
+on: []
+
+env:
+ PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+
+jobs:
+ build-and-publish:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.x'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel twine build requests packaging
+
+ - name: Run version check script
+ id: version_check_script
+ run: |
+ VERSION_INCREMENTED=$(python .github/scripts/check_version_has_incremented.py python ./apps/python-sdk/firecrawl firecrawl-py)
+ echo "VERSION_INCREMENTED=$VERSION_INCREMENTED" >> $GITHUB_ENV
+
+ - name: Build the package
+ if: ${{ env.VERSION_INCREMENTED == 'true' }}
+ run: |
+ python -m build
+ working-directory: ./apps/python-sdk
+
+ - name: Publish to PyPI
+ if: ${{ env.VERSION_INCREMENTED == 'true' }}
+ env:
+ TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ run: |
+ twine upload dist/*
+ working-directory: ./apps/python-sdk
+
diff --git a/.github/archive/python-sdk.yml b/.github/archive/python-sdk.yml
new file mode 100644
index 0000000..2744988
--- /dev/null
+++ b/.github/archive/python-sdk.yml
@@ -0,0 +1,70 @@
+name: Run Python SDK E2E Tests
+
+on: []
+
+env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ BULL_AUTH_KEY: ${{ secrets.BULL_AUTH_KEY }}
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ HOST: ${{ secrets.HOST }}
+ LLAMAPARSE_API_KEY: ${{ secrets.LLAMAPARSE_API_KEY }}
+ LOGTAIL_KEY: ${{ secrets.LOGTAIL_KEY }}
+ POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
+ POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
+ NUM_WORKERS_PER_QUEUE: ${{ secrets.NUM_WORKERS_PER_QUEUE }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ PLAYWRIGHT_MICROSERVICE_URL: ${{ secrets.PLAYWRIGHT_MICROSERVICE_URL }}
+ PORT: ${{ secrets.PORT }}
+ REDIS_URL: ${{ secrets.REDIS_URL }}
+ SCRAPING_BEE_API_KEY: ${{ secrets.SCRAPING_BEE_API_KEY }}
+ SUPABASE_ANON_TOKEN: ${{ secrets.SUPABASE_ANON_TOKEN }}
+ SUPABASE_SERVICE_TOKEN: ${{ secrets.SUPABASE_SERVICE_TOKEN }}
+ SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
+ TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
+ HYPERDX_API_KEY: ${{ secrets.HYPERDX_API_KEY }}
+ HDX_NODE_BETA_MODE: 1
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.10"]
+ services:
+ redis:
+ image: redis
+ ports:
+ - 6379:6379
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: "20"
+ - name: Install pnpm
+ run: npm install -g pnpm
+ - name: Install dependencies for API
+ run: pnpm install
+ working-directory: ./apps/api
+ - name: Start the application
+ run: npm start &
+ working-directory: ./apps/api
+ id: start_app
+ - name: Start workers
+ run: npm run workers &
+ working-directory: ./apps/api
+ id: start_workers
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ working-directory: ./apps/python-sdk
+ - name: Run E2E tests for Python SDK
+ run: |
+ pytest firecrawl/__tests__/e2e_withAuth/test.py
+ working-directory: ./apps/python-sdk
diff --git a/.github/scripts/check_version_has_incremented.py b/.github/scripts/check_version_has_incremented.py
new file mode 100644
index 0000000..e437c93
--- /dev/null
+++ b/.github/scripts/check_version_has_incremented.py
@@ -0,0 +1,88 @@
+"""
+checks local versions against published versions.
+
+# Usage:
+
+python .github/scripts/check_version_has_incremented.py js ./apps/js-sdk/firecrawl @mendable/firecrawl-js
+Local version: 0.0.22
+Published version: 0.0.21
+true
+
+python .github/scripts/check_version_has_incremented.py python ./apps/python-sdk/firecrawl firecrawl-py
+Local version: 0.0.11
+Published version: 0.0.11
+false
+
+"""
+import json
+import os
+import re
+import sys
+from pathlib import Path
+
+import requests
+from packaging.version import Version
+from packaging.version import parse as parse_version
+
+
+def get_python_version(file_path: str) -> str:
+ """Extract version string from Python file."""
+ version_file = Path(file_path).read_text()
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
+ if version_match:
+ return version_match.group(1).strip()
+ raise RuntimeError("Unable to find version string.")
+
+def get_pypi_version(package_name: str) -> str:
+ """Get latest version of Python package from PyPI."""
+ response = requests.get(f"https://pypi.org/pypi/{package_name}/json")
+ version = response.json()['info']['version']
+ return version.strip()
+
+def get_js_version(file_path: str) -> str:
+ """Extract version string from package.json."""
+ with open(file_path, 'r') as file:
+ package_json = json.load(file)
+ if 'version' in package_json:
+ return package_json['version'].strip()
+ raise RuntimeError("Unable to find version string in package.json.")
+
+def get_npm_version(package_name: str) -> str:
+ """Get latest version of JavaScript package from npm."""
+ response = requests.get(f"https://registry.npmjs.org/{package_name}/latest")
+ version = response.json()['version']
+ return version.strip()
+
+def is_version_incremented(local_version: str, published_version: str) -> bool:
+ """Compare local and published versions."""
+ local_version_parsed: Version = parse_version(local_version)
+ published_version_parsed: Version = parse_version(published_version)
+ return local_version_parsed > published_version_parsed
+
+if __name__ == "__main__":
+ package_type = sys.argv[1]
+ package_path = sys.argv[2]
+ package_name = sys.argv[3]
+
+ if package_type == "python":
+ # Get current version from __init__.py
+ current_version = get_python_version(os.path.join(package_path, '__init__.py'))
+ # Get published version from PyPI
+ published_version = get_pypi_version(package_name)
+ elif package_type == "js":
+ # Get current version from package.json
+ current_version = get_js_version(os.path.join(package_path, 'package.json'))
+ # Get published version from npm
+ published_version = get_npm_version(package_name)
+ else:
+ raise ValueError("Invalid package type. Use 'python' or 'js'.")
+
+ # Print versions for debugging
+ # print(f"Local version: {current_version}")
+ # print(f"Published version: {published_version}")
+
+ # Compare versions and print result
+ if is_version_incremented(current_version, published_version):
+ print("true")
+ else:
+ print("false")
diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt
new file mode 100644
index 0000000..0bfc676
--- /dev/null
+++ b/.github/scripts/requirements.txt
@@ -0,0 +1,2 @@
+requests
+packaging
\ No newline at end of file
diff --git a/.github/workflows/clean-before-24h-complete-jobs.yml b/.github/workflows/clean-before-24h-complete-jobs.yml
new file mode 100644
index 0000000..2ced537
--- /dev/null
+++ b/.github/workflows/clean-before-24h-complete-jobs.yml
@@ -0,0 +1,20 @@
+name: Clean Before 24h Completed Jobs
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+env:
+ BULL_AUTH_KEY: ${{ secrets.BULL_AUTH_KEY }}
+
+jobs:
+ clean-jobs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Send GET request to clean jobs
+ run: |
+ response=$(curl --write-out '%{http_code}' --silent --output /dev/null https://api.firecrawl.dev/admin/${{ secrets.BULL_AUTH_KEY }}/clean-before-24h-complete-jobs)
+ if [ "$response" -ne 200 ]; then
+ echo "Failed to clean jobs. Response: $response"
+ exit 1
+ fi
+ echo "Successfully cleaned jobs. Response: $response"
diff --git a/.github/workflows/fly-direct.yml b/.github/workflows/fly-direct.yml
new file mode 100644
index 0000000..a049143
--- /dev/null
+++ b/.github/workflows/fly-direct.yml
@@ -0,0 +1,37 @@
+name: Fly Deploy Direct
+on:
+ schedule:
+ - cron: '0 * * * *'
+
+env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ BULL_AUTH_KEY: ${{ secrets.BULL_AUTH_KEY }}
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ HOST: ${{ secrets.HOST }}
+ LLAMAPARSE_API_KEY: ${{ secrets.LLAMAPARSE_API_KEY }}
+ LOGTAIL_KEY: ${{ secrets.LOGTAIL_KEY }}
+ POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
+ POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
+ NUM_WORKERS_PER_QUEUE: ${{ secrets.NUM_WORKERS_PER_QUEUE }}
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ PLAYWRIGHT_MICROSERVICE_URL: ${{ secrets.PLAYWRIGHT_MICROSERVICE_URL }}
+ PORT: ${{ secrets.PORT }}
+ REDIS_URL: ${{ secrets.REDIS_URL }}
+ SCRAPING_BEE_API_KEY: ${{ secrets.SCRAPING_BEE_API_KEY }}
+ SUPABASE_ANON_TOKEN: ${{ secrets.SUPABASE_ANON_TOKEN }}
+ SUPABASE_SERVICE_TOKEN: ${{ secrets.SUPABASE_SERVICE_TOKEN }}
+ SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
+ TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
+
+jobs:
+ deploy:
+ name: Deploy app
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Change directory
+ run: cd apps/api
+ - uses: superfly/flyctl-actions/setup-flyctl@master
+ - run: flyctl deploy ./apps/api --remote-only -a firecrawl-scraper-js
+ env:
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
diff --git a/.github/workflows/fly.yml b/.github/workflows/fly.yml
index 09d81af..84017b1 100644
--- a/.github/workflows/fly.yml
+++ b/.github/workflows/fly.yml
@@ -3,8 +3,6 @@ on:
push:
branches:
- main
- # schedule:
- # - cron: '0 */4 * * *'
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -25,9 +23,12 @@ env:
SUPABASE_SERVICE_TOKEN: ${{ secrets.SUPABASE_SERVICE_TOKEN }}
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
+ PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
jobs:
- pre-deploy:
+ pre-deploy-e2e-tests:
name: Pre-deploy checks
runs-on: ubuntu-latest
services:
@@ -61,7 +62,7 @@ jobs:
pre-deploy-test-suite:
name: Test Suite
- needs: pre-deploy
+ needs: pre-deploy-e2e-tests
runs-on: ubuntu-latest
services:
redis:
@@ -94,11 +95,84 @@ jobs:
run: |
npm run test
working-directory: ./apps/test-suite
+
+ python-sdk-tests:
+ name: Python SDK Tests
+ needs: pre-deploy-e2e-tests
+ runs-on: ubuntu-latest
+ services:
+ redis:
+ image: redis
+ ports:
+ - 6379:6379
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.x'
+ - name: Install pnpm
+ run: npm install -g pnpm
+ - name: Install dependencies
+ run: pnpm install
+ working-directory: ./apps/api
+ - name: Start the application
+ run: npm start &
+ working-directory: ./apps/api
+ id: start_app
+ - name: Start workers
+ run: npm run workers &
+ working-directory: ./apps/api
+ id: start_workers
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ working-directory: ./apps/python-sdk
+ - name: Run E2E tests for Python SDK
+ run: |
+ pytest firecrawl/__tests__/e2e_withAuth/test.py
+ working-directory: ./apps/python-sdk
+
+ js-sdk-tests:
+ name: JavaScript SDK Tests
+ needs: pre-deploy-e2e-tests
+ runs-on: ubuntu-latest
+ services:
+ redis:
+ image: redis
+ ports:
+ - 6379:6379
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: "20"
+ - name: Install pnpm
+ run: npm install -g pnpm
+ - name: Install dependencies
+ run: pnpm install
+ working-directory: ./apps/api
+ - name: Start the application
+ run: npm start &
+ working-directory: ./apps/api
+ id: start_app
+ - name: Start workers
+ run: npm run workers &
+ working-directory: ./apps/api
+ id: start_workers
+ - name: Install dependencies for JavaScript SDK
+ run: pnpm install
+ working-directory: ./apps/js-sdk/firecrawl
+ - name: Run E2E tests for JavaScript SDK
+ run: npm run test
+ working-directory: ./apps/js-sdk/firecrawl
deploy:
name: Deploy app
runs-on: ubuntu-latest
- needs: pre-deploy-test-suite
+ needs: [pre-deploy-test-suite, python-sdk-tests, js-sdk-tests]
steps:
- uses: actions/checkout@v3
- name: Change directory
@@ -107,3 +181,85 @@ jobs:
- run: flyctl deploy ./apps/api --remote-only -a firecrawl-scraper-js
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+
+ build-and-publish-python-sdk:
+ name: Build and publish Python SDK
+ runs-on: ubuntu-latest
+ needs: deploy
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.x'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel twine build requests packaging
+
+ - name: Run version check script
+ id: version_check_script
+ run: |
+ PYTHON_SDK_VERSION_INCREMENTED=$(python .github/scripts/check_version_has_incremented.py python ./apps/python-sdk/firecrawl firecrawl-py)
+ echo "PYTHON_SDK_VERSION_INCREMENTED=$PYTHON_SDK_VERSION_INCREMENTED" >> $GITHUB_ENV
+
+ - name: Build the package
+ if: ${{ env.PYTHON_SDK_VERSION_INCREMENTED == 'true' }}
+ run: |
+ python -m build
+ working-directory: ./apps/python-sdk
+
+ - name: Publish to PyPI
+ if: ${{ env.PYTHON_SDK_VERSION_INCREMENTED == 'true' }}
+ env:
+ TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ run: |
+ twine upload dist/*
+ working-directory: ./apps/python-sdk
+
+ build-and-publish-js-sdk:
+ name: Build and publish JavaScript SDK
+ runs-on: ubuntu-latest
+ needs: deploy
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '20'
+ registry-url: 'https://registry.npmjs.org/'
+ scope: '@mendable'
+ always-auth: true
+
+ - name: Install pnpm
+ run: npm install -g pnpm
+
+ - name: Install python for running version check script
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel requests packaging
+
+ - name: Install dependencies for JavaScript SDK
+ run: pnpm install
+ working-directory: ./apps/js-sdk/firecrawl
+
+ - name: Run version check script
+ id: version_check_script
+ run: |
+ VERSION_INCREMENTED=$(python .github/scripts/check_version_has_incremented.py js ./apps/js-sdk/firecrawl @mendable/firecrawl-js)
+ echo "VERSION_INCREMENTED=$VERSION_INCREMENTED" >> $GITHUB_ENV
+
+ - name: Build and publish to npm
+ if: ${{ env.VERSION_INCREMENTED == 'true' }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: |
+ npm run build-and-publish
+ working-directory: ./apps/js-sdk/firecrawl
+
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 87d8c28..bf77986 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -39,7 +39,7 @@ SUPABASE_SERVICE_TOKEN=
TEST_API_KEY= # use if you've set up authentication and want to test with a real API key
SCRAPING_BEE_API_KEY= #Set if you'd like to use scraping Be to handle JS blocking
OPENAI_API_KEY= # add for LLM dependednt features (image alt generation, etc.)
-BULL_AUTH_KEY= #
+BULL_AUTH_KEY= @
LOGTAIL_KEY= # Use if you're configuring basic logging with logtail
PLAYWRIGHT_MICROSERVICE_URL= # set if you'd like to run a playwright fallback
LLAMAPARSE_API_KEY= #Set if you have a llamaparse key you'd like to use to parse pdfs
diff --git a/README.md b/README.md
index 2aaeeeb..0aebe1c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# 🔥 Firecrawl
-Crawl and convert any website into LLM-ready markdown. Built by [Mendable.ai](https://mendable.ai?ref=gfirecrawl) and the firecrawl community.
+Crawl and convert any website into LLM-ready markdown or structured data. Built by [Mendable.ai](https://mendable.ai?ref=gfirecrawl) and the Firecrawl community. Includes powerful scraping, crawling and data extraction capabilities.
_This repository is in its early development stages. We are still merging custom modules in the mono repo. It's not completely yet ready for full self-host deployment, but you can already run it locally._
@@ -402,7 +402,6 @@ const searchResults = await app.search(query, {
```
-
## Contributing
We love contributions! Please read our [contributing guide](CONTRIBUTING.md) before submitting a pull request.
diff --git a/SELF_HOST.md b/SELF_HOST.md
index ff5ee04..f74e050 100644
--- a/SELF_HOST.md
+++ b/SELF_HOST.md
@@ -29,3 +29,6 @@ docker compose up
This will run a local instance of Firecrawl which can be accessed at `http://localhost:3002`.
+
+# Install Firecrawl on a Kubernetes Cluster (Simple Version)
+Read the [examples/k8n/README.md](examples/k8n/README.md) for instructions on how to install Firecrawl on a Kubernetes Cluster.
\ No newline at end of file
diff --git a/apps/api/.env.example b/apps/api/.env.example
index 659d68f..c39c8fa 100644
--- a/apps/api/.env.example
+++ b/apps/api/.env.example
@@ -3,7 +3,7 @@ NUM_WORKERS_PER_QUEUE=8
PORT=3002
HOST=0.0.0.0
REDIS_URL=redis://localhost:6379
-PLAYWRIGHT_MICROSERVICE_URL=http://playwright-service:3000
+PLAYWRIGHT_MICROSERVICE_URL=http://playwright-service:3000/html
## To turn on DB authentication, you need to set up supabase.
USE_DB_AUTHENTICATION=true
@@ -21,7 +21,7 @@ RATE_LIMIT_TEST_API_KEY_SCRAPE= # set if you'd like to test the scraping rate li
RATE_LIMIT_TEST_API_KEY_CRAWL= # set if you'd like to test the crawling rate limit
SCRAPING_BEE_API_KEY= #Set if you'd like to use scraping Be to handle JS blocking
OPENAI_API_KEY= # add for LLM dependednt features (image alt generation, etc.)
-BULL_AUTH_KEY= #
+BULL_AUTH_KEY= @
LOGTAIL_KEY= # Use if you're configuring basic logging with logtail
LLAMAPARSE_API_KEY= #Set if you have a llamaparse key you'd like to use to parse pdfs
SERPER_API_KEY= #Set if you have a serper key you'd like to use as a search api
@@ -31,8 +31,28 @@ POSTHOG_HOST= # set if you'd like to send posthog events like job logs
STRIPE_PRICE_ID_STANDARD=
STRIPE_PRICE_ID_SCALE=
+STRIPE_PRICE_ID_STARTER=
+STRIPE_PRICE_ID_HOBBY=
+STRIPE_PRICE_ID_HOBBY_YEARLY=
+STRIPE_PRICE_ID_STANDARD_NEW=
+STRIPE_PRICE_ID_STANDARD_NEW_YEARLY=
+STRIPE_PRICE_ID_GROWTH=
+STRIPE_PRICE_ID_GROWTH_YEARLY=
HYPERDX_API_KEY=
HDX_NODE_BETA_MODE=1
-FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta
\ No newline at end of file
+FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta
+
+# Proxy Settings for Playwright (Alternative you can can use a proxy service like oxylabs, which rotates IPs for you on every request)
+PROXY_SERVER=
+PROXY_USERNAME=
+PROXY_PASSWORD=
+# set if you'd like to block media requests to save proxy bandwidth
+BLOCK_MEDIA=
+
+# Set this to the URL of your webhook when using the self-hosted version of FireCrawl
+SELF_HOSTED_WEBHOOK_URL=
+
+# Resend API Key for transactional emails
+RESEND_API_KEY=
diff --git a/apps/api/fly.toml b/apps/api/fly.toml
index ca619d1..468695d 100644
--- a/apps/api/fly.toml
+++ b/apps/api/fly.toml
@@ -24,8 +24,15 @@ kill_timeout = '5s'
[http_service.concurrency]
type = "requests"
- hard_limit = 200
- soft_limit = 100
+ hard_limit = 100
+ soft_limit = 50
+
+[[http_service.checks]]
+ grace_period = "20s"
+ interval = "30s"
+ method = "GET"
+ timeout = "15s"
+ path = "/"
[[services]]
protocol = 'tcp'
@@ -43,8 +50,8 @@ kill_timeout = '5s'
[services.concurrency]
type = 'connections'
- hard_limit = 75
- soft_limit = 30
+ hard_limit = 30
+ soft_limit = 12
[[vm]]
size = 'performance-4x'
diff --git a/apps/api/openapi.json b/apps/api/openapi.json
index b483bc4..17b3677 100644
--- a/apps/api/openapi.json
+++ b/apps/api/openapi.json
@@ -50,6 +50,27 @@
"type": "boolean",
"description": "Include the raw HTML content of the page. Will output a html key in the response.",
"default": false
+ },
+ "screenshot": {
+ "type": "boolean",
+ "description": "Include a screenshot of the top of the page that you are scraping.",
+ "default": false
+ },
+ "waitFor": {
+ "type": "integer",
+ "description": "Wait x amount of milliseconds for the page to load to fetch content",
+ "default": 0
+ },
+ "removeTags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'"
+ },
+ "headers": {
+ "type": "object",
+ "description": "Headers to send with the request. Can be used to send cookies, user-agent, etc."
}
}
},
@@ -171,10 +192,20 @@
"description": "The crawling mode to use. Fast mode crawls 4x faster websites without sitemap, but may not be as accurate and shouldn't be used in heavy js-rendered websites.",
"default": "default"
},
+ "ignoreSitemap": {
+ "type": "boolean",
+ "description": "Ignore the website sitemap when crawling",
+ "default": false
+ },
"limit": {
"type": "integer",
"description": "Maximum number of pages to crawl",
"default": 10000
+ },
+ "allowBackwardCrawling": {
+ "type": "boolean",
+ "description": "Allow backward crawling (crawl from the base URL to the previous URLs)",
+ "default": false
}
}
},
@@ -190,6 +221,27 @@
"type": "boolean",
"description": "Include the raw HTML content of the page. Will output a html key in the response.",
"default": false
+ },
+ "screenshot": {
+ "type": "boolean",
+ "description": "Include a screenshot of the top of the page that you are scraping.",
+ "default": false
+ },
+ "headers": {
+ "type": "object",
+ "description": "Headers to send with the request when scraping. Can be used to send cookies, user-agent, etc."
+ },
+ "removeTags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags, classes and ids to remove from the page. Use comma separated values. Example: 'script, .ad, #footer'"
+ },
+ "replaceAllPathsWithAbsolutePaths": {
+ "type": "boolean",
+ "description": "Replace all relative paths with absolute paths for images and links",
+ "default": false
}
}
}
@@ -363,7 +415,7 @@
"items": {
"$ref": "#/components/schemas/CrawlStatusResponseObj"
},
- "description": "Partial documents returned as it is being crawls (streaming). When a page is ready it will append to the parial_data array - so no need to wait for all the website to be crawled."
+ "description": "Partial documents returned as it is being crawled (streaming). **This feature is currently in alpha - expect breaking changes** When a page is ready, it will append to the partial_data array, so there is no need to wait for the entire website to be crawled. There is a max of 50 items in the array response. The oldest item (top of the array) will be removed when the new item is added to the array."
}
}
}
@@ -459,7 +511,7 @@
"html": {
"type": "string",
"nullable": true,
- "description": "Raw HTML content of the page if `includeHtml` is true"
+ "description": "Raw HTML content of the page if `includeHtml` is true"
},
"metadata": {
"type": "object",
@@ -474,9 +526,126 @@
"type": "string",
"nullable": true
},
+ "keywords": {
+ "type": "string",
+ "nullable": true
+ },
+ "robots": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogTitle": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogDescription": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogUrl": {
+ "type": "string",
+ "format": "uri",
+ "nullable": true
+ },
+ "ogImage": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogAudio": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogDeterminer": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogLocale": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogLocaleAlternate": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true
+ },
+ "ogSiteName": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogVideo": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsCreated": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDateCreated": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDate": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsType": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcType": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsAudience": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsSubject": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcSubject": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDescription": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsKeywords": {
+ "type": "string",
+ "nullable": true
+ },
+ "modifiedTime": {
+ "type": "string",
+ "nullable": true
+ },
+ "publishedTime": {
+ "type": "string",
+ "nullable": true
+ },
+ "articleTag": {
+ "type": "string",
+ "nullable": true
+ },
+ "articleSection": {
+ "type": "string",
+ "nullable": true
+ },
"sourceURL": {
"type": "string",
"format": "uri"
+ },
+ "pageStatusCode": {
+ "type": "integer",
+ "description": "The status code of the page"
+ },
+ "pageError": {
+ "type": "string",
+ "nullable": true,
+ "description": "The error message of the page"
}
}
},
@@ -508,6 +677,10 @@
"nullable": true,
"description": "Raw HTML content of the page if `includeHtml` is true"
},
+ "index": {
+ "type": "integer",
+ "description": "The number of the page that was crawled. This is useful for `partial_data` so you know which page the data is from."
+ },
"metadata": {
"type": "object",
"properties": {
@@ -521,9 +694,126 @@
"type": "string",
"nullable": true
},
+ "keywords": {
+ "type": "string",
+ "nullable": true
+ },
+ "robots": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogTitle": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogDescription": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogUrl": {
+ "type": "string",
+ "format": "uri",
+ "nullable": true
+ },
+ "ogImage": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogAudio": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogDeterminer": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogLocale": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogLocaleAlternate": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true
+ },
+ "ogSiteName": {
+ "type": "string",
+ "nullable": true
+ },
+ "ogVideo": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsCreated": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDateCreated": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDate": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsType": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcType": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsAudience": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsSubject": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcSubject": {
+ "type": "string",
+ "nullable": true
+ },
+ "dcDescription": {
+ "type": "string",
+ "nullable": true
+ },
+ "dctermsKeywords": {
+ "type": "string",
+ "nullable": true
+ },
+ "modifiedTime": {
+ "type": "string",
+ "nullable": true
+ },
+ "publishedTime": {
+ "type": "string",
+ "nullable": true
+ },
+ "articleTag": {
+ "type": "string",
+ "nullable": true
+ },
+ "articleSection": {
+ "type": "string",
+ "nullable": true
+ },
"sourceURL": {
"type": "string",
"format": "uri"
+ },
+ "pageStatusCode": {
+ "type": "integer",
+ "description": "The status code of the page"
+ },
+ "pageError": {
+ "type": "string",
+ "nullable": true,
+ "description": "The error message of the page"
}
}
}
diff --git a/apps/api/package.json b/apps/api/package.json
index 931c3f2..c786b17 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -30,6 +30,7 @@
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.12",
+ "@types/node": "^20.14.1",
"body-parser": "^1.20.1",
"express": "^4.18.2",
"jest": "^29.6.3",
@@ -90,6 +91,7 @@
"puppeteer": "^22.6.3",
"rate-limiter-flexible": "^2.4.2",
"redis": "^4.6.7",
+ "resend": "^3.2.0",
"robots-parser": "^3.0.1",
"scrapingbee": "^1.7.4",
"stripe": "^12.2.0",
diff --git a/apps/api/pnpm-lock.yaml b/apps/api/pnpm-lock.yaml
index a2d1394..32a9274 100644
--- a/apps/api/pnpm-lock.yaml
+++ b/apps/api/pnpm-lock.yaml
@@ -7,40 +7,40 @@ settings:
dependencies:
'@anthropic-ai/sdk':
specifier: ^0.20.5
- version: 0.20.5
+ version: 0.20.9
'@brillout/import':
specifier: ^0.2.2
version: 0.2.3
'@bull-board/api':
specifier: ^5.14.2
- version: 5.14.2(@bull-board/ui@5.14.2)
+ version: 5.19.2(@bull-board/ui@5.19.2)
'@bull-board/express':
specifier: ^5.8.0
- version: 5.14.2
+ version: 5.19.2
'@devil7softwares/pos':
specifier: ^1.0.2
version: 1.0.2
'@dqbd/tiktoken':
specifier: ^1.0.13
- version: 1.0.13
+ version: 1.0.15
'@hyperdx/node-opentelemetry':
specifier: ^0.7.0
version: 0.7.0
'@logtail/node':
specifier: ^0.4.12
- version: 0.4.20
+ version: 0.4.21
'@nangohq/node':
specifier: ^0.36.33
version: 0.36.101
'@sentry/node':
specifier: ^7.48.0
- version: 7.105.0
+ version: 7.116.0
'@supabase/supabase-js':
specifier: ^2.7.1
- version: 2.39.7
+ version: 2.43.4
ajv:
specifier: ^8.12.0
- version: 8.12.0
+ version: 8.15.0
async:
specifier: ^3.2.5
version: 3.2.5
@@ -49,13 +49,13 @@ dependencies:
version: 0.4.1
axios:
specifier: ^1.3.4
- version: 1.6.7
+ version: 1.7.2
bottleneck:
specifier: ^2.19.5
version: 2.19.5
bull:
specifier: ^4.11.4
- version: 4.12.2
+ version: 4.12.9
cheerio:
specifier: ^1.0.0-rc.12
version: 1.0.0-rc.12
@@ -76,19 +76,19 @@ dependencies:
version: 16.4.5
express-rate-limit:
specifier: ^6.7.0
- version: 6.11.2(express@4.18.3)
+ version: 6.11.2(express@4.19.2)
form-data:
specifier: ^4.0.0
version: 4.0.0
glob:
specifier: ^10.3.12
- version: 10.3.12
+ version: 10.4.1
gpt3-tokenizer:
specifier: ^1.1.5
version: 1.1.5
ioredis:
specifier: ^5.3.2
- version: 5.3.2
+ version: 5.4.1
joplin-turndown-plugin-gfm:
specifier: ^1.0.12
version: 1.0.12
@@ -100,7 +100,7 @@ dependencies:
version: 0.0.25
langchain:
specifier: ^0.1.25
- version: 0.1.25(@supabase/supabase-js@2.39.7)(axios@1.6.7)(cheerio@1.0.0-rc.12)(ioredis@5.3.2)(mammoth@1.7.2)(pdf-parse@1.1.1)(puppeteer@22.6.3)(redis@4.6.13)(typesense@1.7.2)
+ version: 0.1.37(@supabase/supabase-js@2.43.4)(axios@1.7.2)(cheerio@1.0.0-rc.12)(ioredis@5.4.1)(mammoth@1.7.2)(openai@4.47.3)(pdf-parse@1.1.1)(pg@8.11.5)(puppeteer@22.10.0)(redis@4.6.14)(typesense@1.8.2)
languagedetect:
specifier: ^2.0.0
version: 2.0.0
@@ -118,13 +118,13 @@ dependencies:
version: 2.30.1
mongoose:
specifier: ^8.0.3
- version: 8.2.1
+ version: 8.4.1
natural:
specifier: ^6.3.0
- version: 6.10.5
+ version: 6.12.0
openai:
specifier: ^4.28.4
- version: 4.28.4
+ version: 4.47.3
pdf-parse:
specifier: ^1.1.1
version: 1.1.1
@@ -139,13 +139,16 @@ dependencies:
version: 0.0.9
puppeteer:
specifier: ^22.6.3
- version: 22.6.3(typescript@5.4.2)
+ version: 22.10.0(typescript@5.4.5)
rate-limiter-flexible:
specifier: ^2.4.2
version: 2.4.2
redis:
specifier: ^4.6.7
- version: 4.6.13
+ version: 4.6.14
+ resend:
+ specifier: ^3.2.0
+ version: 3.2.0
robots-parser:
specifier: ^3.0.1
version: 3.0.1
@@ -157,13 +160,13 @@ dependencies:
version: 12.18.0
turndown:
specifier: ^7.1.3
- version: 7.1.3
+ version: 7.2.0
turndown-plugin-gfm:
specifier: ^1.0.2
version: 1.0.2
typesense:
specifier: ^1.5.4
- version: 1.7.2(@babel/runtime@7.24.0)
+ version: 1.8.2(@babel/runtime@7.24.6)
unstructured-client:
specifier: ^0.9.4
version: 0.9.4
@@ -178,10 +181,10 @@ dependencies:
version: 0.6.2
zod:
specifier: ^3.23.4
- version: 3.23.4
+ version: 3.23.8
zod-to-json-schema:
specifier: ^3.23.0
- version: 3.23.0(zod@3.23.4)
+ version: 3.23.0(zod@3.23.8)
devDependencies:
'@flydotio/dockerfile':
@@ -189,7 +192,7 @@ devDependencies:
version: 0.4.11
'@tsconfig/recommended':
specifier: ^1.0.3
- version: 1.0.3
+ version: 1.0.6
'@types/body-parser':
specifier: ^1.19.2
version: 1.19.5
@@ -205,15 +208,18 @@ devDependencies:
'@types/jest':
specifier: ^29.5.12
version: 29.5.12
+ '@types/node':
+ specifier: ^20.14.1
+ version: 20.14.1
body-parser:
specifier: ^1.20.1
version: 1.20.2
express:
specifier: ^4.18.2
- version: 4.18.3
+ version: 4.19.2
jest:
specifier: ^29.6.3
- version: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ version: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
jest-fetch-mock:
specifier: ^3.0.3
version: 3.0.3
@@ -225,19 +231,19 @@ devDependencies:
version: 2.0.22
supabase:
specifier: ^1.77.9
- version: 1.148.6
+ version: 1.172.2
supertest:
specifier: ^6.3.3
version: 6.3.4
ts-jest:
specifier: ^29.1.1
- version: 29.1.2(@babel/core@7.24.0)(jest@29.7.0)(typescript@5.4.2)
+ version: 29.1.4(@babel/core@7.24.6)(jest@29.7.0)(typescript@5.4.5)
ts-node:
specifier: ^10.9.1
- version: 10.9.2(@types/node@20.11.25)(typescript@5.4.2)
+ version: 10.9.2(@types/node@20.14.1)(typescript@5.4.5)
typescript:
specifier: ^5.4.2
- version: 5.4.2
+ version: 5.4.5
packages:
@@ -249,10 +255,10 @@ packages:
'@jridgewell/trace-mapping': 0.3.25
dev: true
- /@anthropic-ai/sdk@0.20.5:
- resolution: {integrity: sha512-d0ch+zp6/gHR4+2wqWV7JU1EJ7PpHc3r3F6hebovJTouY+pkaId1FuYYaVsG3l/gyqhOZUwKCMSMqcFNf+ZmWg==}
+ /@anthropic-ai/sdk@0.20.9:
+ resolution: {integrity: sha512-Lq74+DhiEQO6F9/gdVOLmHx57pX45ebK2Q/zH14xYe1157a7QeUVknRqIp0Jz5gQI01o7NKbuv9Dag2uQsLjDg==}
dependencies:
- '@types/node': 18.19.22
+ '@types/node': 18.19.34
'@types/node-fetch': 2.6.11
abort-controller: 3.0.0
agentkeepalive: 4.5.0
@@ -267,7 +273,7 @@ packages:
/@anthropic-ai/sdk@0.9.1:
resolution: {integrity: sha512-wa1meQ2WSfoY8Uor3EdrJq0jTiZJoKoSii2ZVWRY1oN4Tlr5s59pADg9T79FTbPe1/se5c3pBeZgJL63wmuoBA==}
dependencies:
- '@types/node': 18.19.22
+ '@types/node': 18.19.34
'@types/node-fetch': 2.6.11
abort-controller: 3.0.0
agentkeepalive: 4.5.0
@@ -280,34 +286,34 @@ packages:
- encoding
dev: false
- /@babel/code-frame@7.23.5:
- resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+ /@babel/code-frame@7.24.6:
+ resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/highlight': 7.23.4
- chalk: 2.4.2
+ '@babel/highlight': 7.24.6
+ picocolors: 1.0.1
- /@babel/compat-data@7.23.5:
- resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
+ /@babel/compat-data@7.24.6:
+ resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/core@7.24.0:
- resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
+ /@babel/core@7.24.6:
+ resolution: {integrity: sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.3.0
- '@babel/code-frame': 7.23.5
- '@babel/generator': 7.23.6
- '@babel/helper-compilation-targets': 7.23.6
- '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
- '@babel/helpers': 7.24.0
- '@babel/parser': 7.24.0
- '@babel/template': 7.24.0
- '@babel/traverse': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/code-frame': 7.24.6
+ '@babel/generator': 7.24.6
+ '@babel/helper-compilation-targets': 7.24.6
+ '@babel/helper-module-transforms': 7.24.6(@babel/core@7.24.6)
+ '@babel/helpers': 7.24.6
+ '@babel/parser': 7.24.6
+ '@babel/template': 7.24.6
+ '@babel/traverse': 7.24.6
+ '@babel/types': 7.24.6
convert-source-map: 2.0.0
- debug: 4.3.4
+ debug: 4.3.5
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -315,297 +321,295 @@ packages:
- supports-color
dev: true
- /@babel/generator@7.23.6:
- resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
+ /@babel/generator@7.24.6:
+ resolution: {integrity: sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2
dev: true
- /@babel/helper-compilation-targets@7.23.6:
- resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+ /@babel/helper-compilation-targets@7.24.6:
+ resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/compat-data': 7.23.5
- '@babel/helper-validator-option': 7.23.5
+ '@babel/compat-data': 7.24.6
+ '@babel/helper-validator-option': 7.24.6
browserslist: 4.23.0
lru-cache: 5.1.1
semver: 6.3.1
dev: true
- /@babel/helper-environment-visitor@7.22.20:
- resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
+ /@babel/helper-environment-visitor@7.24.6:
+ resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-function-name@7.23.0:
- resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
+ /@babel/helper-function-name@7.24.6:
+ resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/template': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/template': 7.24.6
+ '@babel/types': 7.24.6
dev: true
- /@babel/helper-hoist-variables@7.22.5:
- resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
+ /@babel/helper-hoist-variables@7.24.6:
+ resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
- /@babel/helper-module-imports@7.22.15:
- resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+ /@babel/helper-module-imports@7.24.6:
+ resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
- /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0):
- resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+ /@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6):
+ resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-environment-visitor': 7.22.20
- '@babel/helper-module-imports': 7.22.15
- '@babel/helper-simple-access': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.6
- '@babel/helper-validator-identifier': 7.22.20
+ '@babel/core': 7.24.6
+ '@babel/helper-environment-visitor': 7.24.6
+ '@babel/helper-module-imports': 7.24.6
+ '@babel/helper-simple-access': 7.24.6
+ '@babel/helper-split-export-declaration': 7.24.6
+ '@babel/helper-validator-identifier': 7.24.6
dev: true
- /@babel/helper-plugin-utils@7.24.0:
- resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==}
+ /@babel/helper-plugin-utils@7.24.6:
+ resolution: {integrity: sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-simple-access@7.22.5:
- resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
+ /@babel/helper-simple-access@7.24.6:
+ resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
- /@babel/helper-split-export-declaration@7.22.6:
- resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
+ /@babel/helper-split-export-declaration@7.24.6:
+ resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
- /@babel/helper-string-parser@7.23.4:
- resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+ /@babel/helper-string-parser@7.24.6:
+ resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helper-validator-identifier@7.22.20:
- resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
+ /@babel/helper-validator-identifier@7.24.6:
+ resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==}
engines: {node: '>=6.9.0'}
- /@babel/helper-validator-option@7.23.5:
- resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
+ /@babel/helper-validator-option@7.24.6:
+ resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/helpers@7.24.0:
- resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
+ /@babel/helpers@7.24.6:
+ resolution: {integrity: sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/template': 7.24.0
- '@babel/traverse': 7.24.0
- '@babel/types': 7.24.0
- transitivePeerDependencies:
- - supports-color
+ '@babel/template': 7.24.6
+ '@babel/types': 7.24.6
dev: true
- /@babel/highlight@7.23.4:
- resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+ /@babel/highlight@7.24.6:
+ resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-validator-identifier': 7.22.20
+ '@babel/helper-validator-identifier': 7.24.6
chalk: 2.4.2
js-tokens: 4.0.0
+ picocolors: 1.0.1
- /@babel/parser@7.24.0:
- resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
+ /@babel/parser@7.24.6:
+ resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
- /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0):
+ /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.6):
resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0):
+ /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.6):
resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0):
+ /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.6):
resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0):
- resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
+ /@babel/plugin-syntax-jsx@7.24.6(@babel/core@7.24.6):
+ resolution: {integrity: sha512-lWfvAIFNWMlCsU0DRUun2GpFwZdGTukLaHJqRh1JRb80NdAP5Sb1HDHB5X9P9OtgZHQl089UzQkpYlBq2VTPRw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0):
+ /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.6):
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0):
+ /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.6):
resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0):
+ /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.6):
resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0):
+ /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.6):
resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0):
- resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
+ /@babel/plugin-syntax-typescript@7.24.6(@babel/core@7.24.6):
+ resolution: {integrity: sha512-TzCtxGgVTEJWWwcYwQhCIQ6WaKlo80/B+Onsk4RRCcYqpYGFcG9etPW94VToGte5AAcxRrhjPUFvUS3Y2qKi4A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/helper-plugin-utils': 7.24.6
dev: true
- /@babel/runtime@7.24.0:
- resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==}
+ /@babel/runtime@7.24.6:
+ resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
dev: false
- /@babel/template@7.24.0:
- resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
+ /@babel/template@7.24.6:
+ resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/code-frame': 7.23.5
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/code-frame': 7.24.6
+ '@babel/parser': 7.24.6
+ '@babel/types': 7.24.6
dev: true
- /@babel/traverse@7.24.0:
- resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
+ /@babel/traverse@7.24.6:
+ resolution: {integrity: sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/code-frame': 7.23.5
- '@babel/generator': 7.23.6
- '@babel/helper-environment-visitor': 7.22.20
- '@babel/helper-function-name': 7.23.0
- '@babel/helper-hoist-variables': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.6
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
- debug: 4.3.4
+ '@babel/code-frame': 7.24.6
+ '@babel/generator': 7.24.6
+ '@babel/helper-environment-visitor': 7.24.6
+ '@babel/helper-function-name': 7.24.6
+ '@babel/helper-hoist-variables': 7.24.6
+ '@babel/helper-split-export-declaration': 7.24.6
+ '@babel/parser': 7.24.6
+ '@babel/types': 7.24.6
+ debug: 4.3.5
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/types@7.24.0:
- resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
+ /@babel/types@7.24.6:
+ resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-string-parser': 7.23.4
- '@babel/helper-validator-identifier': 7.22.20
+ '@babel/helper-string-parser': 7.24.6
+ '@babel/helper-validator-identifier': 7.24.6
to-fast-properties: 2.0.0
dev: true
@@ -617,30 +621,30 @@ packages:
resolution: {integrity: sha512-1T8WlD75eeFSMrptGy8jiLHmfHgMmSjWvLOIUvHmSVZt+6k0eQqYUoK4KbmE4T9pVLIfxvZSOm2D68VEqKRHRw==}
dev: false
- /@bull-board/api@5.14.2(@bull-board/ui@5.14.2):
- resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==}
+ /@bull-board/api@5.19.2(@bull-board/ui@5.19.2):
+ resolution: {integrity: sha512-vYXfRo7xTBeBgmC7FOKXLTHBZ/DG9ZsGIaEeE8GAkkEX1DaNKPBJOAn0Z1OmZIeLAe4esbHMFU8O1SDrmV4Hsg==}
peerDependencies:
- '@bull-board/ui': 5.14.2
+ '@bull-board/ui': 5.19.2
dependencies:
- '@bull-board/ui': 5.14.2
+ '@bull-board/ui': 5.19.2
redis-info: 3.1.0
dev: false
- /@bull-board/express@5.14.2:
- resolution: {integrity: sha512-fohMMNzRgHvnoJXeoaD6js6s7YPkjebTth/fHjyWQLngxhcpoIyWXoLavt5BDrgRKEPZaTDwSkIzX4IhrLh2Iw==}
+ /@bull-board/express@5.19.2:
+ resolution: {integrity: sha512-CHTq7RmzE5JQAHP6ohRE6cPDqJnTnBySx5AtmbaZ0oAyhv3iMBz996cRdERrIpyfH3ymSlDrGHqJ2h0c7O629Q==}
dependencies:
- '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
- '@bull-board/ui': 5.14.2
- ejs: 3.1.9
- express: 4.18.3
+ '@bull-board/api': 5.19.2(@bull-board/ui@5.19.2)
+ '@bull-board/ui': 5.19.2
+ ejs: 3.1.10
+ express: 4.19.2
transitivePeerDependencies:
- supports-color
dev: false
- /@bull-board/ui@5.14.2:
- resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==}
+ /@bull-board/ui@5.19.2:
+ resolution: {integrity: sha512-SRNK2ozJplr/5WUB2zT883d0hTb8cs61+eyrBVCJYmTKtA3uP3M8EENu/Gkv9XTZvti420qWlsZKTjOSWmm6Bg==}
dependencies:
- '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
+ '@bull-board/api': 5.19.2(@bull-board/ui@5.19.2)
dev: false
/@colors/colors@1.6.0:
@@ -661,8 +665,8 @@ packages:
deprecated: This package has been renamed to `fast-tag-pos`
dev: false
- /@dqbd/tiktoken@1.0.13:
- resolution: {integrity: sha512-941kjlHjfI97l6NuH/AwuXV4mHuVnRooDcHNSlzi98hz+4ug3wT4gJcWjSwSZHqeGAEn90lC9sFD+8a9d5Jvxg==}
+ /@dqbd/tiktoken@1.0.15:
+ resolution: {integrity: sha512-a6I67K1xUkuqcuwulobIJiLikkoE7egMaviI1Jg5bxSn2V7QGqXsGE3jTKr8UIOU/o74mAAd5TkeXFNBtaKF4A==}
dev: false
/@flydotio/dockerfile@0.4.11:
@@ -672,7 +676,7 @@ packages:
dependencies:
chalk: 5.3.0
diff: 5.2.0
- ejs: 3.1.9
+ ejs: 3.1.10
shell-quote: 1.8.1
yargs: 17.7.2
dev: true
@@ -715,7 +719,7 @@ packages:
'@opentelemetry/sdk-node': 0.51.1(@opentelemetry/api@1.8.0)
'@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
'@opentelemetry/semantic-conventions': 1.24.1
- debug: 4.3.4
+ debug: 4.3.5
json-stringify-safe: 5.0.1
lodash.isobject: 3.0.2
lodash.isplainobject: 4.0.6
@@ -742,7 +746,13 @@ packages:
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
- dev: false
+
+ /@isaacs/fs-minipass@4.0.1:
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+ dependencies:
+ minipass: 7.1.2
+ dev: true
/@istanbuljs/load-nyc-config@1.1.0:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
@@ -765,7 +775,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
@@ -786,14 +796,14 @@ packages:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
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.11.25)(ts-node@10.9.2)
+ jest-config: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -805,7 +815,7 @@ packages:
jest-util: 29.7.0
jest-validate: 29.7.0
jest-watcher: 29.7.0
- micromatch: 4.0.5
+ micromatch: 4.0.7
pretty-format: 29.7.0
slash: 3.0.0
strip-ansi: 6.0.1
@@ -821,7 +831,7 @@ packages:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
jest-mock: 29.7.0
dev: true
@@ -848,7 +858,7 @@ packages:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -881,7 +891,7 @@ packages:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.25
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
collect-v8-coverage: 1.0.2
exit: 0.1.2
@@ -943,7 +953,7 @@ packages:
resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.6
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.25
babel-plugin-istanbul: 6.1.1
@@ -954,7 +964,7 @@ packages:
jest-haste-map: 29.7.0
jest-regex-util: 29.6.3
jest-util: 29.7.0
- micromatch: 4.0.5
+ micromatch: 4.0.7
pirates: 4.0.6
slash: 3.0.0
write-file-atomic: 4.0.2
@@ -969,7 +979,7 @@ packages:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
'@types/yargs': 17.0.32
chalk: 4.1.2
dev: true
@@ -1015,8 +1025,8 @@ packages:
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
dev: false
- /@langchain/community@0.0.35(@supabase/supabase-js@2.39.7)(ioredis@5.3.2)(redis@4.6.13)(typesense@1.7.2):
- resolution: {integrity: sha512-xZGjiqlS7X0EDWM67s2PxSLg0Rz/Wfc741IPF0Ok/f4yFwFseWjtcWXwBwe0dVnapIstpKR82q+RDAa06xFxyw==}
+ /@langchain/community@0.0.57(@supabase/supabase-js@2.43.4)(ioredis@5.4.1)(langchain@0.1.37)(openai@4.47.3)(pg@8.11.5)(redis@4.6.14)(typesense@1.8.2):
+ resolution: {integrity: sha512-tib4UJNkyA4TPNsTNChiBtZmThVJBr7X/iooSmKeCr+yUEha2Yxly3A4OAO95Vlpj4Q+od8HAfCbZih/1XqAMw==}
engines: {node: '>=18'}
peerDependencies:
'@aws-crypto/sha256-js': ^5.0.0
@@ -1031,7 +1041,7 @@ packages:
'@azure/search-documents': ^12.0.0
'@clickhouse/client': ^0.2.5
'@cloudflare/ai': '*'
- '@datastax/astra-db-ts': ^0.1.4
+ '@datastax/astra-db-ts': ^1.0.0
'@elastic/elasticsearch': ^8.4.0
'@getmetal/metal-sdk': '*'
'@getzep/zep-js': ^0.9.0
@@ -1040,11 +1050,14 @@ packages:
'@google-ai/generativelanguage': ^0.2.1
'@gradientai/nodejs-sdk': ^1.2.0
'@huggingface/inference': ^2.6.4
+ '@mlc-ai/web-llm': ^0.2.35
'@mozilla/readability': '*'
+ '@neondatabase/serverless': '*'
'@opensearch-project/opensearch': '*'
'@pinecone-database/pinecone': '*'
'@planetscale/database': ^1.8.0
- '@qdrant/js-client-rest': ^1.2.0
+ '@premai/prem-sdk': ^0.3.25
+ '@qdrant/js-client-rest': ^1.8.2
'@raycast/api': ^1.55.2
'@rockset/client': ^0.9.1
'@smithy/eventstream-codec': ^2.0.5
@@ -1057,7 +1070,7 @@ packages:
'@tensorflow/tfjs-converter': '*'
'@tensorflow/tfjs-core': '*'
'@upstash/redis': ^1.20.6
- '@upstash/vector': ^1.0.2
+ '@upstash/vector': ^1.0.7
'@vercel/kv': ^0.2.3
'@vercel/postgres': ^0.5.0
'@writerai/writer-sdk': ^0.40.2
@@ -1066,22 +1079,28 @@ packages:
'@zilliz/milvus2-sdk-node': '>=2.2.7'
better-sqlite3: ^9.4.0
cassandra-driver: ^4.7.2
+ cborg: ^4.1.1
chromadb: '*'
closevector-common: 0.1.3
closevector-node: 0.1.6
closevector-web: 0.1.6
cohere-ai: '*'
convex: ^1.3.1
+ couchbase: ^4.3.0
discord.js: ^14.14.1
dria: ^0.0.3
+ duck-duck-scrape: ^2.2.5
faiss-node: ^0.5.1
firebase-admin: ^11.9.0 || ^12.0.0
google-auth-library: ^8.9.0
googleapis: ^126.0.1
- hnswlib-node: ^1.4.2
+ hnswlib-node: ^3.0.0
html-to-text: ^9.0.5
+ interface-datastore: ^8.2.11
ioredis: ^5.3.2
+ it-all: ^3.0.4
jsdom: '*'
+ jsonwebtoken: ^9.0.2
llmonitor: ^0.5.9
lodash: ^4.17.21
lunary: ^0.6.11
@@ -1146,14 +1165,20 @@ packages:
optional: true
'@huggingface/inference':
optional: true
+ '@mlc-ai/web-llm':
+ optional: true
'@mozilla/readability':
optional: true
+ '@neondatabase/serverless':
+ optional: true
'@opensearch-project/opensearch':
optional: true
'@pinecone-database/pinecone':
optional: true
'@planetscale/database':
optional: true
+ '@premai/prem-sdk':
+ optional: true
'@qdrant/js-client-rest':
optional: true
'@raycast/api':
@@ -1198,6 +1223,8 @@ packages:
optional: true
cassandra-driver:
optional: true
+ cborg:
+ optional: true
chromadb:
optional: true
closevector-common:
@@ -1210,10 +1237,14 @@ packages:
optional: true
convex:
optional: true
+ couchbase:
+ optional: true
discord.js:
optional: true
dria:
optional: true
+ duck-duck-scrape:
+ optional: true
faiss-node:
optional: true
firebase-admin:
@@ -1226,10 +1257,16 @@ packages:
optional: true
html-to-text:
optional: true
+ interface-datastore:
+ optional: true
ioredis:
optional: true
+ it-all:
+ optional: true
jsdom:
optional: true
+ jsonwebtoken:
+ optional: true
llmonitor:
optional: true
lodash:
@@ -1273,62 +1310,83 @@ packages:
ws:
optional: true
dependencies:
- '@langchain/core': 0.1.43
- '@langchain/openai': 0.0.18
- '@supabase/supabase-js': 2.39.7
+ '@langchain/core': 0.1.63(langchain@0.1.37)(openai@4.47.3)
+ '@langchain/openai': 0.0.34(langchain@0.1.37)
+ '@supabase/supabase-js': 2.43.4
+ expr-eval: 2.0.2
flat: 5.0.2
- ioredis: 5.3.2
- langsmith: 0.1.13
- redis: 4.6.13
- typesense: 1.7.2(@babel/runtime@7.24.0)
+ ioredis: 5.4.1
+ langsmith: 0.1.30(@langchain/core@0.1.63)(langchain@0.1.37)(openai@4.47.3)
+ pg: 8.11.5
+ redis: 4.6.14
+ typesense: 1.8.2(@babel/runtime@7.24.6)
uuid: 9.0.1
- zod: 3.23.4
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.0(zod@3.23.8)
transitivePeerDependencies:
- encoding
+ - langchain
+ - openai
dev: false
- /@langchain/core@0.1.43:
- resolution: {integrity: sha512-owE+UU38e4TsUq5yoaKCF+ag6u0ppwgdaqEt2Q57pdcr9nEcy8/PgTunxB10Vksq4fTJgnwWEYf/wMGZnFlRow==}
+ /@langchain/core@0.1.63(langchain@0.1.37)(openai@4.47.3):
+ resolution: {integrity: sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w==}
engines: {node: '>=18'}
dependencies:
ansi-styles: 5.2.0
camelcase: 6.3.0
decamelize: 1.2.0
- js-tiktoken: 1.0.10
- langsmith: 0.1.13
+ js-tiktoken: 1.0.12
+ langsmith: 0.1.30(@langchain/core@0.1.63)(langchain@0.1.37)(openai@4.47.3)
ml-distance: 4.0.1
+ mustache: 4.2.0
p-queue: 6.6.2
p-retry: 4.6.2
uuid: 9.0.1
- zod: 3.23.4
- zod-to-json-schema: 3.23.0(zod@3.23.4)
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.0(zod@3.23.8)
+ transitivePeerDependencies:
+ - langchain
+ - openai
dev: false
- /@langchain/openai@0.0.18:
- resolution: {integrity: sha512-SBY1PlwiHIcjW185yVXHo4XXgTVAyGxw7IHpuEqs7201/EVjFW91HskzGRvduYm2td3/NV91BBVFgXhJQcvtmA==}
+ /@langchain/openai@0.0.34(langchain@0.1.37):
+ resolution: {integrity: sha512-M+CW4oXle5fdoz2T2SwdOef8pl3/1XmUx1vjn2mXUVM/128aO0l23FMF0SNBsAbRV6P+p/TuzjodchJbi0Ht/A==}
engines: {node: '>=18'}
dependencies:
- '@langchain/core': 0.1.43
- js-tiktoken: 1.0.10
- openai: 4.28.4
- zod: 3.23.4
- zod-to-json-schema: 3.23.0(zod@3.23.4)
+ '@langchain/core': 0.1.63(langchain@0.1.37)(openai@4.47.3)
+ js-tiktoken: 1.0.12
+ openai: 4.47.3
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.0(zod@3.23.8)
transitivePeerDependencies:
- encoding
+ - langchain
dev: false
- /@logtail/core@0.4.20:
- resolution: {integrity: sha512-6Ij6InFtz5rF/cBjdr3UUCN3jSgCP08syJPYZV+tyVSpUyUtOPP1L5ln2B5ooIsBT3uJtjV5LbGUm1zeOkh3+A==}
+ /@langchain/textsplitters@0.0.2(langchain@0.1.37)(openai@4.47.3):
+ resolution: {integrity: sha512-6bQOuYHTGYlkgPY/8M5WPq4nnXZpEysGzRopQCYjg2WLcEoIPUMMrXsAaNNdvU3BOeMrhin8izvpDPD165hX6Q==}
+ engines: {node: '>=18'}
dependencies:
- '@logtail/tools': 0.4.20
+ '@langchain/core': 0.1.63(langchain@0.1.37)(openai@4.47.3)
+ js-tiktoken: 1.0.12
+ transitivePeerDependencies:
+ - langchain
+ - openai
+ dev: false
+
+ /@logtail/core@0.4.21:
+ resolution: {integrity: sha512-QDq194+24bwi4e+a/pxyf4X67NewhTvBmh9iwM2NhbSVSQz4Fo8xQn1Ul8zuUrXETycu/Od2D8wT2tZFNFx/7A==}
+ dependencies:
+ '@logtail/tools': 0.4.21
'@logtail/types': 0.4.20
serialize-error: 8.1.0
dev: false
- /@logtail/node@0.4.20:
- resolution: {integrity: sha512-KJegdSKIAk3e8XMEuj6Yb+jIpDKdWyzaRZAECo6++Nyl6zLB4p6xYRNB39lPhDI2YqyzEE4d4N8p6kWVfzJ2Tg==}
+ /@logtail/node@0.4.21:
+ resolution: {integrity: sha512-zpwkhJgcYaM+vsjotHRJthc0ot1vP0CAVy+fwrkL8XjfdC3NHiWb6f0agQpHlqdRX8RTsAbcYpWNXKPpFB5U9Q==}
dependencies:
- '@logtail/core': 0.4.20
+ '@logtail/core': 0.4.21
'@logtail/types': 0.4.20
'@msgpack/msgpack': 2.8.0
'@types/stack-trace': 0.0.29
@@ -1340,8 +1398,8 @@ packages:
- encoding
dev: false
- /@logtail/tools@0.4.20:
- resolution: {integrity: sha512-EBqet6u78Byekwo4tBbJ0ZkcYkCefFhxZCvz+l8WORGtcpDq/8bfMZoE4BSnr9Y/jerCxuCarJ6Feqyd5A2ioA==}
+ /@logtail/tools@0.4.21:
+ resolution: {integrity: sha512-xIaolScUwJEikllopGphxBX0lVlN/rA8pLAZiNCMNJXpPbwitoFKLW3w4qRuYdKoFCCJZKwOdwEqU2Fv0i9Cuw==}
dependencies:
'@logtail/types': 0.4.20
dev: false
@@ -1350,8 +1408,12 @@ packages:
resolution: {integrity: sha512-nYsum10eJMTo+ySBlYXvSrvgD1NDCVUeOlxLBbelq3XUmHu9L48VNR3P0BOmhLamYCTEgjatTj0PyPLfjL1W9g==}
dev: false
- /@mongodb-js/saslprep@1.1.5:
- resolution: {integrity: sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==}
+ /@mixmark-io/domino@2.2.0:
+ resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
+ dev: false
+
+ /@mongodb-js/saslprep@1.1.7:
+ resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==}
dependencies:
sparse-bitfield: 3.0.3
dev: false
@@ -1407,11 +1469,15 @@ packages:
resolution: {integrity: sha512-ott5k+3MsieVyx5kxFVpfMQWNLI8sdQWrzF+aiLh3tnJsRo7iEAAmPLT8VH1hXNMYr8KeRJJb8a043WIpjvnaQ==}
engines: {node: '>=16.7'}
dependencies:
- axios: 1.6.7
+ axios: 1.7.2
transitivePeerDependencies:
- debug
dev: false
+ /@one-ini/wasm@0.1.1:
+ resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+ dev: false
+
/@opentelemetry/api-logs@0.51.1:
resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==}
engines: {node: '>=14'}
@@ -1723,7 +1789,7 @@ packages:
'@opentelemetry/api': 1.8.0
'@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
'@opentelemetry/semantic-conventions': 1.24.1
- semver: 7.6.0
+ semver: 7.6.2
transitivePeerDependencies:
- supports-color
dev: false
@@ -1831,7 +1897,7 @@ packages:
'@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
'@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
'@opentelemetry/semantic-conventions': 1.24.1
- semver: 7.6.0
+ semver: 7.6.2
transitivePeerDependencies:
- supports-color
dev: false
@@ -2134,7 +2200,7 @@ packages:
'@types/shimmer': 1.0.5
import-in-the-middle: 1.7.4
require-in-the-middle: 7.3.0
- semver: 7.6.0
+ semver: 7.6.2
shimmer: 1.2.1
transitivePeerDependencies:
- supports-color
@@ -2379,7 +2445,7 @@ packages:
'@opentelemetry/propagator-b3': 1.24.1(@opentelemetry/api@1.8.0)
'@opentelemetry/propagator-jaeger': 1.24.1(@opentelemetry/api@1.8.0)
'@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
- semver: 7.6.0
+ semver: 7.6.2
dev: false
/@opentelemetry/semantic-conventions@1.24.1:
@@ -2401,7 +2467,6 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
- dev: false
optional: true
/@protobufjs/aspromise@1.1.2:
@@ -2447,8 +2512,8 @@ packages:
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
dev: false
- /@puppeteer/browsers@2.2.1:
- resolution: {integrity: sha512-QSXujx4d4ogDamQA8ckkkRieFzDgZEuZuGiey9G7CuDcbnX4iINKWxTPC5Br2AEzY9ICAvcndqgAUFMMKnS/Tw==}
+ /@puppeteer/browsers@2.2.3:
+ resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==}
engines: {node: '>=18'}
hasBin: true
dependencies:
@@ -2464,16 +2529,26 @@ packages:
- supports-color
dev: false
- /@redis/bloom@1.2.0(@redis/client@1.5.14):
+ /@react-email/render@0.0.12:
+ resolution: {integrity: sha512-S8WRv/PqECEi6x0QJBj0asnAb5GFtJaHlnByxLETLkgJjc76cxMYDH4r9wdbuJ4sjkcbpwP3LPnVzwS+aIjT7g==}
+ engines: {node: '>=18.0.0'}
+ dependencies:
+ html-to-text: 9.0.5
+ js-beautify: 1.15.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@redis/bloom@1.2.0(@redis/client@1.5.16):
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
- '@redis/client': 1.5.14
+ '@redis/client': 1.5.16
dev: false
- /@redis/client@1.5.14:
- resolution: {integrity: sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==}
+ /@redis/client@1.5.16:
+ resolution: {integrity: sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==}
engines: {node: '>=14'}
dependencies:
cluster-key-slot: 1.1.2
@@ -2481,75 +2556,93 @@ packages:
yallist: 4.0.0
dev: false
- /@redis/graph@1.1.1(@redis/client@1.5.14):
+ /@redis/graph@1.1.1(@redis/client@1.5.16):
resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
- '@redis/client': 1.5.14
+ '@redis/client': 1.5.16
dev: false
- /@redis/json@1.0.6(@redis/client@1.5.14):
+ /@redis/json@1.0.6(@redis/client@1.5.16):
resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
- '@redis/client': 1.5.14
+ '@redis/client': 1.5.16
dev: false
- /@redis/search@1.1.6(@redis/client@1.5.14):
+ /@redis/search@1.1.6(@redis/client@1.5.16):
resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
- '@redis/client': 1.5.14
+ '@redis/client': 1.5.16
dev: false
- /@redis/time-series@1.0.5(@redis/client@1.5.14):
+ /@redis/time-series@1.0.5(@redis/client@1.5.16):
resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
- '@redis/client': 1.5.14
+ '@redis/client': 1.5.16
dev: false
- /@sentry-internal/tracing@7.105.0:
- resolution: {integrity: sha512-b+AFYB7Bc9vmyxl2jbmuT4esX5G0oPfpz35A0sxFzmJIhvMg1YMDNio2c81BtKN+VSPORCnKMLhfk3kyKKvWMQ==}
+ /@selderee/plugin-htmlparser2@0.11.0:
+ resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
+ dependencies:
+ domhandler: 5.0.3
+ selderee: 0.11.0
+ dev: false
+
+ /@sentry-internal/tracing@7.116.0:
+ resolution: {integrity: sha512-y5ppEmoOlfr77c/HqsEXR72092qmGYS4QE5gSz5UZFn9CiinEwGfEorcg2xIrrCuU7Ry/ZU2VLz9q3xd04drRA==}
engines: {node: '>=8'}
dependencies:
- '@sentry/core': 7.105.0
- '@sentry/types': 7.105.0
- '@sentry/utils': 7.105.0
+ '@sentry/core': 7.116.0
+ '@sentry/types': 7.116.0
+ '@sentry/utils': 7.116.0
dev: false
- /@sentry/core@7.105.0:
- resolution: {integrity: sha512-5xsaTG6jZincTeJUmZomlv20mVRZUEF1U/g89lmrSOybyk2+opEnB1JeBn4ODwnvmSik8r2QLr6/RiYlaxRJCg==}
+ /@sentry/core@7.116.0:
+ resolution: {integrity: sha512-J6Wmjjx+o7RwST0weTU1KaKUAlzbc8MGkJV1rcHM9xjNTWTva+nrcCM3vFBagnk2Gm/zhwv3h0PvWEqVyp3U1Q==}
engines: {node: '>=8'}
dependencies:
- '@sentry/types': 7.105.0
- '@sentry/utils': 7.105.0
+ '@sentry/types': 7.116.0
+ '@sentry/utils': 7.116.0
dev: false
- /@sentry/node@7.105.0:
- resolution: {integrity: sha512-b0QwZ7vT4hcJi6LmNRh3dcaYpLtXnkYXkL0rfhMb8hN8sUx8zuOWFMI7j0cfAloVThUeJVwGyv9dERfzGS2r2w==}
+ /@sentry/integrations@7.116.0:
+ resolution: {integrity: sha512-UZb60gaF+7veh1Yv79RiGvgGYOnU6xA97H+hI6tKgc1uT20YpItO4X56Vhp0lvyEyUGFZzBRRH1jpMDPNGPkqw==}
engines: {node: '>=8'}
dependencies:
- '@sentry-internal/tracing': 7.105.0
- '@sentry/core': 7.105.0
- '@sentry/types': 7.105.0
- '@sentry/utils': 7.105.0
+ '@sentry/core': 7.116.0
+ '@sentry/types': 7.116.0
+ '@sentry/utils': 7.116.0
+ localforage: 1.10.0
dev: false
- /@sentry/types@7.105.0:
- resolution: {integrity: sha512-80o0KMVM+X2Ym9hoQxvJetkJJwkpCg7o6tHHFXI+Rp7fawc2iCMTa0IRQMUiSkFvntQLYIdDoNNuKdzz2PbQGA==}
- engines: {node: '>=8'}
- dev: false
-
- /@sentry/utils@7.105.0:
- resolution: {integrity: sha512-YVAV0c2KLM8+VZCicQ/E/P2+J9Vs0hGhrXwV7w6ZEAtvxrg4oF270toL1WRhvcaf8JO4J1v4V+LuU6Txs4uEeQ==}
+ /@sentry/node@7.116.0:
+ resolution: {integrity: sha512-HB/4TrJWbnu6swNzkid+MlwzLwY/D/klGt3R0aatgrgWPo2jJm6bSl4LUT39Cr2eg5I1gsREQtXE2mAlC6gm8w==}
engines: {node: '>=8'}
dependencies:
- '@sentry/types': 7.105.0
+ '@sentry-internal/tracing': 7.116.0
+ '@sentry/core': 7.116.0
+ '@sentry/integrations': 7.116.0
+ '@sentry/types': 7.116.0
+ '@sentry/utils': 7.116.0
+ dev: false
+
+ /@sentry/types@7.116.0:
+ resolution: {integrity: sha512-QCCvG5QuQrwgKzV11lolNQPP2k67Q6HHD9vllZ/C4dkxkjoIym8Gy+1OgAN3wjsR0f/kG9o5iZyglgNpUVRapQ==}
+ engines: {node: '>=8'}
+ dev: false
+
+ /@sentry/utils@7.116.0:
+ resolution: {integrity: sha512-Vn9fcvwTq91wJvCd7WTMWozimqMi+dEZ3ie3EICELC2diONcN16ADFdzn65CQQbYwmUzRjN9EjDN2k41pKZWhQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ '@sentry/types': 7.116.0
dev: false
/@sinclair/typebox@0.27.8:
@@ -2568,14 +2661,14 @@ packages:
'@sinonjs/commons': 3.0.1
dev: true
- /@supabase/functions-js@2.1.5:
- resolution: {integrity: sha512-BNzC5XhCzzCaggJ8s53DP+WeHHGT/NfTsx2wUSSGKR2/ikLFQTBCDzMvGz/PxYMqRko/LwncQtKXGOYp1PkPaw==}
+ /@supabase/auth-js@2.64.2:
+ resolution: {integrity: sha512-s+lkHEdGiczDrzXJ1YWt2y3bxRi+qIUnXcgkpLSrId7yjBeaXBFygNjTaoZLG02KNcYwbuZ9qkEIqmj2hF7svw==}
dependencies:
'@supabase/node-fetch': 2.6.15
dev: false
- /@supabase/gotrue-js@2.62.2:
- resolution: {integrity: sha512-AP6e6W9rQXFTEJ7sTTNYQrNf0LCcnt1hUW+RIgUK+Uh3jbWvcIST7wAlYyNZiMlS9+PYyymWQ+Ykz/rOYSO0+A==}
+ /@supabase/functions-js@2.3.1:
+ resolution: {integrity: sha512-QyzNle/rVzlOi4BbVqxLSH828VdGY1RElqGFAj+XeVypj6+PVtMlD21G8SDnsPQDtlqqTtoGRgdMlQZih5hTuw==}
dependencies:
'@supabase/node-fetch': 2.6.15
dev: false
@@ -2587,19 +2680,19 @@ packages:
whatwg-url: 5.0.0
dev: false
- /@supabase/postgrest-js@1.9.2:
- resolution: {integrity: sha512-I6yHo8CC9cxhOo6DouDMy9uOfW7hjdsnCxZiaJuIVZm1dBGTFiQPgfMa9zXCamEWzNyWRjZvupAUuX+tqcl5Sw==}
+ /@supabase/postgrest-js@1.15.2:
+ resolution: {integrity: sha512-9/7pUmXExvGuEK1yZhVYXPZnLEkDTwxgMQHXLrN5BwPZZm4iUCL1YEyep/Z2lIZah8d8M433mVAUEGsihUj5KQ==}
dependencies:
'@supabase/node-fetch': 2.6.15
dev: false
- /@supabase/realtime-js@2.9.3:
- resolution: {integrity: sha512-lAp50s2n3FhGJFq+wTSXLNIDPw5Y0Wxrgt44eM5nLSA3jZNUUP3Oq2Ccd1CbZdVntPCWLZvJaU//pAd2NE+QnQ==}
+ /@supabase/realtime-js@2.9.5:
+ resolution: {integrity: sha512-TEHlGwNGGmKPdeMtca1lFTYCedrhTAv3nZVoSjrKQ+wkMmaERuCe57zkC5KSWFzLYkb5FVHW8Hrr+PX1DDwplQ==}
dependencies:
'@supabase/node-fetch': 2.6.15
'@types/phoenix': 1.6.4
'@types/ws': 8.5.10
- ws: 8.16.0
+ ws: 8.17.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -2611,14 +2704,14 @@ packages:
'@supabase/node-fetch': 2.6.15
dev: false
- /@supabase/supabase-js@2.39.7:
- resolution: {integrity: sha512-1vxsX10Uhc2b+Dv9pRjBjHfqmw2N2h1PyTg9LEfICR3x2xwE24By1MGCjDZuzDKH5OeHCsf4it6K8KRluAAEXA==}
+ /@supabase/supabase-js@2.43.4:
+ resolution: {integrity: sha512-/pLPaxiIsn5Vaz3s32HC6O/VNwfeddnzS0bZRpOW0AKcPuXroD8pT9G8mpiBlZfpKsMmq6k7tlhW7Sr1PAQ1lw==}
dependencies:
- '@supabase/functions-js': 2.1.5
- '@supabase/gotrue-js': 2.62.2
+ '@supabase/auth-js': 2.64.2
+ '@supabase/functions-js': 2.3.1
'@supabase/node-fetch': 2.6.15
- '@supabase/postgrest-js': 1.9.2
- '@supabase/realtime-js': 2.9.3
+ '@supabase/postgrest-js': 1.15.2
+ '@supabase/realtime-js': 2.9.5
'@supabase/storage-js': 2.5.5
transitivePeerDependencies:
- bufferutil
@@ -2629,8 +2722,8 @@ packages:
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
dev: false
- /@tsconfig/node10@1.0.9:
- resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
+ /@tsconfig/node10@1.0.11:
+ resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
dev: true
/@tsconfig/node12@1.0.11:
@@ -2645,14 +2738,14 @@ packages:
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
dev: true
- /@tsconfig/recommended@1.0.3:
- resolution: {integrity: sha512-+jby/Guq9H8O7NWgCv6X8VAiQE8Dr/nccsCtL74xyHKhu2Knu5EAKmOZj3nLCnLm1KooUzKY+5DsnGVqhM8/wQ==}
+ /@tsconfig/recommended@1.0.6:
+ resolution: {integrity: sha512-0IKu9GHYF1NGTJiYgfWwqnOQSlnE9V9R7YohHNNf0/fj/SyOZWzdd06JFr0fLpg1Mqw0kGbYg8w5xdkSqLKM9g==}
dev: true
/@types/accepts@1.3.7:
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/aws-lambda@8.10.122:
@@ -2662,43 +2755,43 @@ packages:
/@types/babel__core@7.20.5:
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
dependencies:
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/parser': 7.24.6
+ '@babel/types': 7.24.6
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
- '@types/babel__traverse': 7.20.5
+ '@types/babel__traverse': 7.20.6
dev: true
/@types/babel__generator@7.6.8:
resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
/@types/babel__template@7.4.4:
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
dependencies:
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/parser': 7.24.6
+ '@babel/types': 7.24.6
dev: true
- /@types/babel__traverse@7.20.5:
- resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==}
+ /@types/babel__traverse@7.20.6:
+ resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
dependencies:
- '@babel/types': 7.24.0
+ '@babel/types': 7.24.6
dev: true
/@types/body-parser@1.19.5:
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
dependencies:
'@types/connect': 3.4.38
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
/@types/bull@4.10.0:
resolution: {integrity: sha512-RkYW8K2H3J76HT6twmHYbzJ0GtLDDotpLP9ah9gtiA7zfF6peBH1l5fEiK0oeIZ3/642M7Jcb9sPmor8Vf4w6g==}
deprecated: This is a stub types definition. bull provides its own type definitions, so you do not need this installed.
dependencies:
- bull: 4.12.2
+ bull: 4.12.9
transitivePeerDependencies:
- supports-color
dev: true
@@ -2706,19 +2799,19 @@ packages:
/@types/bunyan@1.8.9:
resolution: {integrity: sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/connect@3.4.36:
resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/connect@3.4.38:
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
/@types/content-disposition@0.5.8:
resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
@@ -2730,20 +2823,20 @@ packages:
'@types/connect': 3.4.38
'@types/express': 4.17.21
'@types/keygrip': 1.0.6
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/cors@2.8.17:
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: true
- /@types/express-serve-static-core@4.17.43:
- resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==}
+ /@types/express-serve-static-core@4.19.3:
+ resolution: {integrity: sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==}
dependencies:
- '@types/node': 20.11.25
- '@types/qs': 6.9.12
+ '@types/node': 20.14.1
+ '@types/qs': 6.9.15
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -2751,14 +2844,14 @@ packages:
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
dependencies:
'@types/body-parser': 1.19.5
- '@types/express-serve-static-core': 4.17.43
- '@types/qs': 6.9.12
- '@types/serve-static': 1.15.5
+ '@types/express-serve-static-core': 4.19.3
+ '@types/qs': 6.9.15
+ '@types/serve-static': 1.15.7
/@types/graceful-fs@4.1.9:
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: true
/@types/http-assert@1.5.5:
@@ -2811,7 +2904,7 @@ packages:
'@types/http-errors': 2.0.4
'@types/keygrip': 1.0.6
'@types/koa-compose': 3.2.8
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/koa__router@12.0.3:
@@ -2823,36 +2916,33 @@ packages:
/@types/memcached@2.2.10:
resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/mime@1.3.5:
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
- /@types/mime@3.0.4:
- resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==}
-
/@types/mysql@2.15.22:
resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/node-fetch@2.6.11:
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
dependencies:
- '@types/node': 18.19.22
+ '@types/node': 20.14.1
form-data: 4.0.0
dev: false
- /@types/node@18.19.22:
- resolution: {integrity: sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==}
+ /@types/node@18.19.34:
+ resolution: {integrity: sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g==}
dependencies:
undici-types: 5.26.5
dev: false
- /@types/node@20.11.25:
- resolution: {integrity: sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==}
+ /@types/node@20.14.1:
+ resolution: {integrity: sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==}
dependencies:
undici-types: 5.26.5
@@ -2865,7 +2955,7 @@ packages:
/@types/pg@8.6.1:
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
pg-protocol: 1.6.1
pg-types: 2.2.0
dev: false
@@ -2874,8 +2964,8 @@ packages:
resolution: {integrity: sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==}
dev: false
- /@types/qs@6.9.12:
- resolution: {integrity: sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==}
+ /@types/qs@6.9.15:
+ resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==}
/@types/range-parser@1.2.7:
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
@@ -2888,14 +2978,14 @@ packages:
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
dependencies:
'@types/mime': 1.3.5
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
- /@types/serve-static@1.15.5:
- resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==}
+ /@types/serve-static@1.15.7:
+ resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==}
dependencies:
'@types/http-errors': 2.0.4
- '@types/mime': 3.0.4
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
+ '@types/send': 0.17.4
/@types/shimmer@1.0.5:
resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==}
@@ -2912,7 +3002,7 @@ packages:
/@types/tedious@4.0.14:
resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/triple-beam@1.3.5:
@@ -2927,8 +3017,8 @@ packages:
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
dev: false
- /@types/whatwg-url@11.0.4:
- resolution: {integrity: sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==}
+ /@types/whatwg-url@11.0.5:
+ resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==}
dependencies:
'@types/webidl-conversions': 7.0.3
dev: false
@@ -2936,7 +3026,7 @@ packages:
/@types/ws@8.5.10:
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
/@types/yargs-parser@21.0.3:
@@ -2953,7 +3043,7 @@ packages:
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
requiresBuild: true
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
dev: false
optional: true
@@ -2961,9 +3051,10 @@ packages:
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
engines: {node: '>=10.0.0'}
- /abbrev@1.1.1:
- resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
- dev: true
+ /abbrev@2.0.0:
+ resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dev: false
/abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
@@ -3005,22 +3096,13 @@ packages:
resolution: {integrity: sha512-7+Wlx3BImrK0HiG6y3lU4xX7SpBPSSu8T9iguPMlaueRFxjbYwAQrp9lqZUuFikqKbd/en8lVREILvP2J80uJA==}
dev: false
- /agent-base@7.1.0:
- resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
- engines: {node: '>= 14'}
- dependencies:
- debug: 4.3.4
- transitivePeerDependencies:
- - supports-color
-
/agent-base@7.1.1:
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
engines: {node: '>= 14'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
- dev: false
/agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
@@ -3029,13 +3111,13 @@ packages:
humanize-ms: 1.2.1
dev: false
- /ajv@8.12.0:
- resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+ /ajv@8.15.0:
+ resolution: {integrity: sha512-15BTtQUOsSrmHCy+B4VnAiJAJxJ8IFgu6fcjFQF3jQYZ78nLSQthlFg4ehp+NLIyfvFgOlxNsjKIEhydtFPVHQ==}
dependencies:
fast-deep-equal: 3.1.3
+ fast-uri: 2.3.0
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
- uri-js: 4.4.1
dev: false
/ansi-escapes@4.3.2:
@@ -3052,7 +3134,6 @@ packages:
/ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
- dev: false
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
@@ -3073,7 +3154,6 @@ packages:
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
- dev: false
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
@@ -3136,22 +3216,22 @@ packages:
/axios-retry@3.9.1:
resolution: {integrity: sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w==}
dependencies:
- '@babel/runtime': 7.24.0
+ '@babel/runtime': 7.24.6
is-retry-allowed: 2.2.0
dev: false
/axios@0.26.1:
resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==}
dependencies:
- follow-redirects: 1.15.5
+ follow-redirects: 1.15.6
transitivePeerDependencies:
- debug
dev: false
- /axios@1.6.7:
- resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==}
+ /axios@1.7.2:
+ resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
dependencies:
- follow-redirects: 1.15.5
+ follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -3162,17 +3242,17 @@ packages:
resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
dev: false
- /babel-jest@29.7.0(@babel/core@7.24.0):
+ /babel-jest@29.7.0(@babel/core@7.24.6):
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
'@babel/core': ^7.8.0
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.6
'@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.24.0)
+ babel-preset-jest: 29.6.3(@babel/core@7.24.6)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
@@ -3184,7 +3264,7 @@ packages:
resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
engines: {node: '>=8'}
dependencies:
- '@babel/helper-plugin-utils': 7.24.0
+ '@babel/helper-plugin-utils': 7.24.6
'@istanbuljs/load-nyc-config': 1.1.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-instrument: 5.2.1
@@ -3197,73 +3277,81 @@ packages:
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@babel/template': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/template': 7.24.6
+ '@babel/types': 7.24.6
'@types/babel__core': 7.20.5
- '@types/babel__traverse': 7.20.5
+ '@types/babel__traverse': 7.20.6
dev: true
- /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.0):
+ /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.6):
resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
- '@babel/core': 7.24.0
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0)
- '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0)
+ '@babel/core': 7.24.6
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.6)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.6)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.6)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.6)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.6)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.6)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.6)
dev: true
- /babel-preset-jest@29.6.3(@babel/core@7.24.0):
+ /babel-preset-jest@29.6.3(@babel/core@7.24.6):
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
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.6
babel-plugin-jest-hoist: 29.6.3
- babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.0)
+ babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.6)
dev: true
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
- /bare-events@2.2.2:
- resolution: {integrity: sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==}
+ /bare-events@2.3.1:
+ resolution: {integrity: sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA==}
requiresBuild: true
dev: false
optional: true
- /bare-fs@2.2.3:
- resolution: {integrity: sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==}
+ /bare-fs@2.3.1:
+ resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==}
requiresBuild: true
dependencies:
- bare-events: 2.2.2
- bare-path: 2.1.1
- streamx: 2.16.1
+ bare-events: 2.3.1
+ bare-path: 2.1.3
+ bare-stream: 2.0.1
dev: false
optional: true
- /bare-os@2.2.1:
- resolution: {integrity: sha512-OwPyHgBBMkhC29Hl3O4/YfxW9n7mdTr2+SsO29XBWKKJsbgj3mnorDB80r5TiCQgQstgE5ga1qNYrpes6NvX2w==}
+ /bare-os@2.3.0:
+ resolution: {integrity: sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==}
requiresBuild: true
dev: false
optional: true
- /bare-path@2.1.1:
- resolution: {integrity: sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==}
+ /bare-path@2.1.3:
+ resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==}
requiresBuild: true
dependencies:
- bare-os: 2.2.1
+ bare-os: 2.3.0
+ dev: false
+ optional: true
+
+ /bare-stream@2.0.1:
+ resolution: {integrity: sha512-ubLyoDqPnUf5o0kSFp709HC0WRZuxVuh4pbte5eY95Xvx5bdvz07c2JFmXBfqqe60q+9PJ8S4X5GRvmcNSKMxg==}
+ requiresBuild: true
+ dependencies:
+ streamx: 2.18.0
dev: false
optional: true
@@ -3283,18 +3371,18 @@ packages:
resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
dev: false
- /bin-links@4.0.3:
- resolution: {integrity: sha512-obsRaULtJurnfox/MDwgq6Yo9kzbv1CPTk/1/s7Z/61Lezc8IKkFCOXNeVLXz0456WRzBQmSsDWlai2tIhBsfA==}
+ /bin-links@4.0.4:
+ resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dependencies:
- cmd-shim: 6.0.2
+ cmd-shim: 6.0.3
npm-normalize-package-bin: 3.0.1
read-cmd-shim: 4.0.0
write-file-atomic: 5.0.1
dev: true
- /binary-extensions@2.2.0:
- resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ /binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
/binary-search@1.3.6:
@@ -3342,11 +3430,11 @@ packages:
dependencies:
balanced-match: 1.0.2
- /braces@3.0.2:
- resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+ /braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
dependencies:
- fill-range: 7.0.1
+ fill-range: 7.1.1
dev: true
/browserslist@4.23.0:
@@ -3354,10 +3442,10 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
- caniuse-lite: 1.0.30001596
- electron-to-chromium: 1.4.695
+ caniuse-lite: 1.0.30001627
+ electron-to-chromium: 1.4.789
node-releases: 2.0.14
- update-browserslist-db: 1.0.13(browserslist@4.23.0)
+ update-browserslist-db: 1.0.16(browserslist@4.23.0)
dev: true
/bs-logger@0.2.6:
@@ -3373,8 +3461,8 @@ packages:
node-int64: 0.4.0
dev: true
- /bson@6.4.0:
- resolution: {integrity: sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==}
+ /bson@6.7.0:
+ resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==}
engines: {node: '>=16.20.1'}
dev: false
@@ -3400,16 +3488,16 @@ packages:
ieee754: 1.2.1
dev: false
- /bull@4.12.2:
- resolution: {integrity: sha512-WPuc0VCYx+cIVMiZtPwRpWyyJFBrj4/OgKJ6n9Jf4tIw7rQNV+HAKQv15UDkcTvfpGFehvod7Fd1YztbYSJIDQ==}
+ /bull@4.12.9:
+ resolution: {integrity: sha512-rqka/O9ZBfrKgI4fanhN6XW0AJ9WYRakjHlCJPjoHyh79xIvEjyU8hvs/CCeRdrbU6zSw8UNfDOjCUaQO1MTuQ==}
engines: {node: '>=12'}
dependencies:
cron-parser: 4.9.0
get-port: 5.1.1
- ioredis: 5.3.2
+ ioredis: 5.4.1
lodash: 4.17.21
- msgpackr: 1.10.1
- semver: 7.6.0
+ msgpackr: 1.10.2
+ semver: 7.6.2
uuid: 8.3.2
transitivePeerDependencies:
- supports-color
@@ -3426,7 +3514,7 @@ packages:
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.2.4
- set-function-length: 1.2.1
+ set-function-length: 1.2.2
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@@ -3441,8 +3529,8 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
- /caniuse-lite@1.0.30001596:
- resolution: {integrity: sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==}
+ /caniuse-lite@1.0.30001627:
+ resolution: {integrity: sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==}
dev: true
/chalk@2.4.2:
@@ -3503,7 +3591,7 @@ packages:
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
- braces: 3.0.2
+ braces: 3.0.3
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
@@ -3513,17 +3601,17 @@ packages:
fsevents: 2.3.3
dev: true
- /chownr@2.0.0:
- resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
- engines: {node: '>=10'}
+ /chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
dev: true
- /chromium-bidi@0.5.16(devtools-protocol@0.0.1262051):
- resolution: {integrity: sha512-IT5lnR44h/qZQ4GaCHvBxYIl4cQL2i9UvFyYeRyVdcpY04hx5H720HQfe/7Oz7ndxaYVLQFGpCO71J4X2Ye/Gw==}
+ /chromium-bidi@0.5.19(devtools-protocol@0.0.1286932):
+ resolution: {integrity: sha512-UA6zL77b7RYCjJkZBsZ0wlvCTD+jTjllZ8f6wdO4buevXgTZYjV+XLB9CiEa2OuuTGGTLnI7eN9I60YxuALGQg==}
peerDependencies:
devtools-protocol: '*'
dependencies:
- devtools-protocol: 0.0.1262051
+ devtools-protocol: 0.0.1286932
mitt: 3.0.1
urlpattern-polyfill: 10.0.0
zod: 3.22.4
@@ -3534,8 +3622,8 @@ packages:
engines: {node: '>=8'}
dev: true
- /cjs-module-lexer@1.2.3:
- resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==}
+ /cjs-module-lexer@1.3.1:
+ resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==}
/class-transformer@0.5.1:
resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
@@ -3553,8 +3641,8 @@ packages:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
- /cmd-shim@6.0.2:
- resolution: {integrity: sha512-+FFYbB0YLaAkhkcrjkyNLYDiOsFSfRjwjY19LXk/psmMx1z00xlCv7hhQoTGXXIKi+YXHL/iiFo8NqMVQX9nOw==}
+ /cmd-shim@6.0.3:
+ resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
@@ -3610,6 +3698,13 @@ packages:
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ /config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+ dev: false
+
/content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
@@ -3627,8 +3722,8 @@ packages:
/cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
- /cookie@0.5.0:
- resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+ /cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
/cookiejar@2.1.4:
@@ -3646,7 +3741,7 @@ packages:
vary: 1.1.2
dev: false
- /cosmiconfig@9.0.0(typescript@5.4.2):
+ /cosmiconfig@9.0.0(typescript@5.4.5):
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
engines: {node: '>=14'}
peerDependencies:
@@ -3659,10 +3754,10 @@ packages:
import-fresh: 3.3.0
js-yaml: 4.1.0
parse-json: 5.2.0
- typescript: 5.4.2
+ typescript: 5.4.5
dev: false
- /create-jest@29.7.0(@types/node@20.11.25)(ts-node@10.9.2):
+ /create-jest@29.7.0(@types/node@20.14.1)(ts-node@10.9.2):
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -3671,7 +3766,7 @@ packages:
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ jest-config: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -3725,8 +3820,8 @@ packages:
engines: {node: '>= 6'}
dev: false
- /csv-parse@5.5.5:
- resolution: {integrity: sha512-erCk7tyU3yLWAhk6wvKxnyPtftuy/6Ak622gOO7BCJ05+TYffnPCJF905wmOQm+BpkX54OdAl8pveJwUdpnCXQ==}
+ /csv-parse@5.5.6:
+ resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==}
dev: false
/data-uri-to-buffer@4.0.1:
@@ -3743,7 +3838,7 @@ packages:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
dependencies:
- '@babel/runtime': 7.24.0
+ '@babel/runtime': 7.24.6
dev: false
/debug@2.6.9:
@@ -3777,6 +3872,18 @@ packages:
optional: true
dependencies:
ms: 2.1.2
+ dev: false
+
+ /debug@4.3.5:
+ resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.2
/decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
@@ -3788,8 +3895,8 @@ packages:
engines: {node: '>=10'}
dev: false
- /dedent@1.5.1:
- resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
+ /dedent@1.5.3:
+ resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==}
peerDependencies:
babel-plugin-macros: ^3.1.0
peerDependenciesMeta:
@@ -3843,8 +3950,8 @@ packages:
engines: {node: '>=8'}
dev: true
- /devtools-protocol@0.0.1262051:
- resolution: {integrity: sha512-YJe4CT5SA8on3Spa+UDtNhEqtuV6Epwz3OZ4HQVLhlRccpZ9/PAYk0/cy/oKxFKRrZPBUPyxympQci4yWNWZ9g==}
+ /devtools-protocol@0.0.1286932:
+ resolution: {integrity: sha512-wu58HMQll9voDjR4NlPyoDEw1syfzaBNHymMMZ/QOXiHRNluOnDgu9hp1yHOKYoMlxCh4lSSiugLITe6Fvu1eA==}
dev: false
/dezalgo@1.0.4:
@@ -3898,10 +4005,6 @@ packages:
domelementtype: 2.3.0
dev: false
- /domino@2.1.6:
- resolution: {integrity: sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==}
- dev: false
-
/domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
dependencies:
@@ -3922,20 +4025,30 @@ packages:
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ /editorconfig@1.0.4:
+ resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dependencies:
+ '@one-ini/wasm': 0.1.1
+ commander: 10.0.1
+ minimatch: 9.0.1
+ semver: 7.6.2
dev: false
/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- /ejs@3.1.9:
- resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
+ /ejs@3.1.10:
+ resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
engines: {node: '>=0.10.0'}
hasBin: true
dependencies:
- jake: 10.8.7
+ jake: 10.9.1
- /electron-to-chromium@1.4.695:
- resolution: {integrity: sha512-eMijZmeqPtm774pCZIOrfUHMs/7ls++W1sLhxwqgu8KQ8E2WmMtzwyqOMt0XXUJ3HTIPfuwlfwF+I5cwnfItBA==}
+ /electron-to-chromium@1.4.789:
+ resolution: {integrity: sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==}
dev: true
/emittery@0.13.1:
@@ -3948,7 +4061,6 @@ packages:
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
- dev: false
/encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
@@ -4110,17 +4222,17 @@ packages:
resolution: {integrity: sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==}
dev: false
- /express-rate-limit@6.11.2(express@4.18.3):
+ /express-rate-limit@6.11.2(express@4.19.2):
resolution: {integrity: sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==}
engines: {node: '>= 14'}
peerDependencies:
express: ^4 || ^5
dependencies:
- express: 4.18.3
+ express: 4.19.2
dev: false
- /express@4.18.3:
- resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==}
+ /express@4.19.2:
+ resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
engines: {node: '>= 0.10.0'}
dependencies:
accepts: 1.3.8
@@ -4128,7 +4240,7 @@ packages:
body-parser: 1.20.2
content-disposition: 0.5.4
content-type: 1.0.5
- cookie: 0.5.0
+ cookie: 0.6.0
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
@@ -4195,6 +4307,10 @@ packages:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
dev: true
+ /fast-uri@2.3.0:
+ resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==}
+ dev: false
+
/fb-watchman@2.0.2:
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
dependencies:
@@ -4224,8 +4340,8 @@ packages:
dependencies:
minimatch: 5.1.6
- /fill-range@7.0.1:
- resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+ /fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
@@ -4258,8 +4374,8 @@ packages:
hasBin: true
dev: false
- /follow-redirects@1.15.5:
- resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
+ /follow-redirects@1.15.6:
+ resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
@@ -4274,7 +4390,6 @@ packages:
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
- dev: false
/form-data-encoder@1.7.2:
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
@@ -4309,7 +4424,7 @@ packages:
dezalgo: 1.0.4
hexoid: 1.0.0
once: 1.4.0
- qs: 6.12.0
+ qs: 6.12.1
dev: true
/forwarded@0.2.0:
@@ -4329,13 +4444,6 @@ packages:
universalify: 2.0.1
dev: false
- /fs-minipass@2.1.0:
- resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
- engines: {node: '>= 8'}
- dependencies:
- minipass: 3.3.6
- dev: true
-
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
@@ -4398,7 +4506,7 @@ packages:
function-bind: 1.1.2
has-proto: 1.0.3
has-symbols: 1.0.3
- hasown: 2.0.1
+ hasown: 2.0.2
/get-package-type@0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
@@ -4440,20 +4548,20 @@ packages:
is-glob: 4.0.3
dev: true
- /glob@10.3.12:
- resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
- engines: {node: '>=16 || 14 >=14.17'}
+ /glob@10.4.1:
+ resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==}
+ engines: {node: '>=16 || 14 >=14.18'}
hasBin: true
dependencies:
foreground-child: 3.1.1
- jackspeak: 2.3.6
+ jackspeak: 3.2.3
minimatch: 9.0.4
- minipass: 7.0.4
- path-scurry: 1.10.2
- dev: false
+ minipass: 7.1.2
+ path-scurry: 1.11.1
/glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
@@ -4517,8 +4625,8 @@ packages:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
- /hasown@2.0.1:
- resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
+ /hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
dependencies:
function-bind: 1.1.2
@@ -4532,6 +4640,17 @@ packages:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
dev: true
+ /html-to-text@9.0.5:
+ resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@selderee/plugin-htmlparser2': 0.11.0
+ deepmerge: 4.3.1
+ dom-serializer: 2.0.0
+ htmlparser2: 8.0.2
+ selderee: 0.11.0
+ dev: false
+
/htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
dependencies:
@@ -4555,7 +4674,7 @@ packages:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
dependencies:
- agent-base: 7.1.0
+ agent-base: 7.1.1
debug: 4.3.4
transitivePeerDependencies:
- supports-color
@@ -4565,8 +4684,8 @@ packages:
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
engines: {node: '>= 14'}
dependencies:
- agent-base: 7.1.0
- debug: 4.3.4
+ agent-base: 7.1.1
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
@@ -4611,7 +4730,7 @@ packages:
dependencies:
acorn: 8.11.3
acorn-import-attributes: 1.9.5(acorn@8.11.3)
- cjs-module-lexer: 1.2.3
+ cjs-module-lexer: 1.3.1
module-details-from-path: 1.0.3
dev: false
@@ -4631,6 +4750,7 @@ packages:
/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.
dependencies:
once: 1.4.0
wrappy: 1.0.2
@@ -4639,13 +4759,17 @@ packages:
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
- /ioredis@5.3.2:
- resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==}
+ /ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+ dev: false
+
+ /ioredis@5.4.1:
+ resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
engines: {node: '>=12.22.0'}
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
- debug: 4.3.4
+ debug: 4.3.5
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -4678,7 +4802,7 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
- binary-extensions: 2.2.0
+ binary-extensions: 2.3.0
dev: true
/is-buffer@1.1.6:
@@ -4688,7 +4812,7 @@ packages:
/is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
dependencies:
- hasown: 2.0.1
+ hasown: 2.0.2
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
@@ -4759,8 +4883,8 @@ packages:
resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
engines: {node: '>=8'}
dependencies:
- '@babel/core': 7.24.0
- '@babel/parser': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/parser': 7.24.6
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
@@ -4772,11 +4896,11 @@ packages:
resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==}
engines: {node: '>=10'}
dependencies:
- '@babel/core': 7.24.0
- '@babel/parser': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/parser': 7.24.6
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
- semver: 7.6.0
+ semver: 7.6.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -4794,7 +4918,7 @@ packages:
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
engines: {node: '>=10'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -4809,17 +4933,16 @@ packages:
istanbul-lib-report: 3.0.1
dev: true
- /jackspeak@2.3.6:
- resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
+ /jackspeak@3.2.3:
+ resolution: {integrity: sha512-htOzIMPbpLid/Gq9/zaz9SfExABxqRe1sSCdxntlO/aMD6u0issZQiY25n2GKQUtJ02j7z5sfptlAOMpWWOmvw==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
- dev: false
- /jake@10.8.7:
- resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
+ /jake@10.9.1:
+ resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==}
engines: {node: '>=10'}
hasBin: true
dependencies:
@@ -4845,10 +4968,10 @@ packages:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
co: 4.6.0
- dedent: 1.5.1
+ dedent: 1.5.3
is-generator-fn: 2.1.0
jest-each: 29.7.0
jest-matcher-utils: 29.7.0
@@ -4858,7 +4981,7 @@ packages:
jest-util: 29.7.0
p-limit: 3.1.0
pretty-format: 29.7.0
- pure-rand: 6.0.4
+ pure-rand: 6.1.0
slash: 3.0.0
stack-utils: 2.0.6
transitivePeerDependencies:
@@ -4866,7 +4989,7 @@ packages:
- supports-color
dev: true
- /jest-cli@29.7.0(@types/node@20.11.25)(ts-node@10.9.2):
+ /jest-cli@29.7.0(@types/node@20.14.1)(ts-node@10.9.2):
resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -4880,10 +5003,10 @@ packages:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
- create-jest: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ create-jest: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
exit: 0.1.2
import-local: 3.1.0
- jest-config: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ jest-config: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -4894,7 +5017,7 @@ packages:
- ts-node
dev: true
- /jest-config@29.7.0(@types/node@20.11.25)(ts-node@10.9.2):
+ /jest-config@29.7.0(@types/node@20.14.1)(ts-node@10.9.2):
resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
peerDependencies:
@@ -4906,11 +5029,11 @@ packages:
ts-node:
optional: true
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.6
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
- babel-jest: 29.7.0(@babel/core@7.24.0)
+ '@types/node': 20.14.1
+ babel-jest: 29.7.0(@babel/core@7.24.6)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
@@ -4924,12 +5047,12 @@ packages:
jest-runner: 29.7.0
jest-util: 29.7.0
jest-validate: 29.7.0
- micromatch: 4.0.5
+ micromatch: 4.0.7
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
- ts-node: 10.9.2(@types/node@20.11.25)(typescript@5.4.2)
+ ts-node: 10.9.2(@types/node@20.14.1)(typescript@5.4.5)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -4970,7 +5093,7 @@ packages:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
jest-mock: 29.7.0
jest-util: 29.7.0
dev: true
@@ -4995,14 +5118,14 @@ packages:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
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.5
+ micromatch: 4.0.7
walker: 1.0.8
optionalDependencies:
fsevents: 2.3.3
@@ -5030,12 +5153,12 @@ packages:
resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@babel/code-frame': 7.23.5
+ '@babel/code-frame': 7.24.6
'@jest/types': 29.6.3
'@types/stack-utils': 2.0.3
chalk: 4.1.2
graceful-fs: 4.2.11
- micromatch: 4.0.5
+ micromatch: 4.0.7
pretty-format: 29.7.0
slash: 3.0.0
stack-utils: 2.0.6
@@ -5046,7 +5169,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
jest-util: 29.7.0
dev: true
@@ -5101,7 +5224,7 @@ packages:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -5132,9 +5255,9 @@ packages:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
- cjs-module-lexer: 1.2.3
+ cjs-module-lexer: 1.3.1
collect-v8-coverage: 1.0.2
glob: 7.2.3
graceful-fs: 4.2.11
@@ -5155,15 +5278,15 @@ packages:
resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@babel/core': 7.24.0
- '@babel/generator': 7.23.6
- '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0)
- '@babel/types': 7.24.0
+ '@babel/core': 7.24.6
+ '@babel/generator': 7.24.6
+ '@babel/plugin-syntax-jsx': 7.24.6(@babel/core@7.24.6)
+ '@babel/plugin-syntax-typescript': 7.24.6(@babel/core@7.24.6)
+ '@babel/types': 7.24.6
'@jest/expect-utils': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.0)
+ babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.6)
chalk: 4.1.2
expect: 29.7.0
graceful-fs: 4.2.11
@@ -5174,7 +5297,7 @@ packages:
jest-util: 29.7.0
natural-compare: 1.4.0
pretty-format: 29.7.0
- semver: 7.6.0
+ semver: 7.6.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -5184,7 +5307,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -5209,7 +5332,7 @@ packages:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@@ -5221,13 +5344,13 @@ packages:
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
dev: true
- /jest@29.7.0(@types/node@20.11.25)(ts-node@10.9.2):
+ /jest@29.7.0(@types/node@20.14.1)(ts-node@10.9.2):
resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
hasBin: true
@@ -5240,7 +5363,7 @@ packages:
'@jest/core': 29.7.0(ts-node@10.9.2)
'@jest/types': 29.6.3
import-local: 3.1.0
- jest-cli: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ jest-cli: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -5252,8 +5375,25 @@ packages:
resolution: {integrity: sha512-qL4+1iycQjZ1fs8zk3jSRk7cg3ROBUHk7GKtiLAQLFzLPKErnILUvz5DLszSQvz3s1sTjPbywLDISVUtBY6HaA==}
dev: false
- /js-tiktoken@1.0.10:
- resolution: {integrity: sha512-ZoSxbGjvGyMT13x6ACo9ebhDha/0FHdKA+OsQcMOWcm1Zs7r90Rhk5lhERLzji+3rA7EKpXCgwXcM5fF3DMpdA==}
+ /js-beautify@1.15.1:
+ resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dependencies:
+ config-chain: 1.1.13
+ editorconfig: 1.0.4
+ glob: 10.4.1
+ js-cookie: 3.0.5
+ nopt: 7.2.1
+ dev: false
+
+ /js-cookie@3.0.5:
+ resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+ engines: {node: '>=14'}
+ dev: false
+
+ /js-tiktoken@1.0.12:
+ resolution: {integrity: sha512-L7wURW1fH9Qaext0VzaUDpFGVQgjkdE3Dgsy9/+yXyGEpBKnylTd0mU0bfbNkKDlXRb6TEsZkwuflu1B8uQbJQ==}
dependencies:
base64-js: 1.5.1
dev: false
@@ -5343,8 +5483,8 @@ packages:
readable-stream: 2.3.8
setimmediate: 1.0.5
- /kareem@2.5.1:
- resolution: {integrity: sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==}
+ /kareem@2.6.3:
+ resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==}
engines: {node: '>=12.0.0'}
dev: false
@@ -5358,8 +5498,8 @@ packages:
engines: {node: '>=6'}
dev: true
- /langchain@0.1.25(@supabase/supabase-js@2.39.7)(axios@1.6.7)(cheerio@1.0.0-rc.12)(ioredis@5.3.2)(mammoth@1.7.2)(pdf-parse@1.1.1)(puppeteer@22.6.3)(redis@4.6.13)(typesense@1.7.2):
- resolution: {integrity: sha512-sfEChvr4H2CklHdSByNBbytwBrFhgtA5kPOnwcBrxuXGg1iOaTzhVxQA0QcNcQucI3hZrsNbZjxGp+Can1ooZQ==}
+ /langchain@0.1.37(@supabase/supabase-js@2.43.4)(axios@1.7.2)(cheerio@1.0.0-rc.12)(ioredis@5.4.1)(mammoth@1.7.2)(openai@4.47.3)(pdf-parse@1.1.1)(pg@8.11.5)(puppeteer@22.10.0)(redis@4.6.14)(typesense@1.8.2):
+ resolution: {integrity: sha512-rpaLEJtRrLYhAViEp7/aHfSkxbgSqHJ5n10tXv3o4kHP/wOin85RpTgewwvGjEaKc3797jOg+sLSk6a7e0UlMg==}
engines: {node: '>=18'}
peerDependencies:
'@aws-sdk/client-s3': ^3.310.0
@@ -5367,11 +5507,13 @@ packages:
'@aws-sdk/client-sfn': ^3.310.0
'@aws-sdk/credential-provider-node': ^3.388.0
'@azure/storage-blob': ^12.15.0
+ '@browserbasehq/sdk': '*'
'@gomomento/sdk': ^1.51.1
'@gomomento/sdk-core': ^1.51.1
'@gomomento/sdk-web': ^1.51.1
'@google-ai/generativelanguage': ^0.2.1
'@google-cloud/storage': ^6.10.1 || ^7.7.0
+ '@mendable/firecrawl-js': ^0.0.13
'@notionhq/client': ^2.2.10
'@pinecone-database/pinecone': '*'
'@supabase/supabase-js': ^2.10.0
@@ -5383,11 +5525,11 @@ packages:
cheerio: ^1.0.0-rc.12
chromadb: '*'
convex: ^1.3.1
- couchbase: ^4.2.10
+ couchbase: ^4.3.0
d3-dsv: ^2.0.0
epub2: ^3.0.1
faiss-node: '*'
- fast-xml-parser: ^4.2.7
+ fast-xml-parser: '*'
google-auth-library: ^8.9.0
handlebars: ^4.7.8
html-to-text: ^9.0.5
@@ -5424,6 +5566,8 @@ packages:
optional: true
'@azure/storage-blob':
optional: true
+ '@browserbasehq/sdk':
+ optional: true
'@gomomento/sdk':
optional: true
'@gomomento/sdk-core':
@@ -5434,6 +5578,8 @@ packages:
optional: true
'@google-cloud/storage':
optional: true
+ '@mendable/firecrawl-js':
+ optional: true
'@notionhq/client':
optional: true
'@pinecone-database/pinecone':
@@ -5518,31 +5664,31 @@ packages:
optional: true
dependencies:
'@anthropic-ai/sdk': 0.9.1
- '@langchain/community': 0.0.35(@supabase/supabase-js@2.39.7)(ioredis@5.3.2)(redis@4.6.13)(typesense@1.7.2)
- '@langchain/core': 0.1.43
- '@langchain/openai': 0.0.18
- '@supabase/supabase-js': 2.39.7
- axios: 1.6.7
- binary-extensions: 2.2.0
+ '@langchain/community': 0.0.57(@supabase/supabase-js@2.43.4)(ioredis@5.4.1)(langchain@0.1.37)(openai@4.47.3)(pg@8.11.5)(redis@4.6.14)(typesense@1.8.2)
+ '@langchain/core': 0.1.63(langchain@0.1.37)(openai@4.47.3)
+ '@langchain/openai': 0.0.34(langchain@0.1.37)
+ '@langchain/textsplitters': 0.0.2(langchain@0.1.37)(openai@4.47.3)
+ '@supabase/supabase-js': 2.43.4
+ axios: 1.7.2
+ binary-extensions: 2.3.0
cheerio: 1.0.0-rc.12
- expr-eval: 2.0.2
- ioredis: 5.3.2
- js-tiktoken: 1.0.10
+ ioredis: 5.4.1
+ js-tiktoken: 1.0.12
js-yaml: 4.1.0
jsonpointer: 5.0.1
- langchainhub: 0.0.8
- langsmith: 0.1.13
+ langchainhub: 0.0.11
+ langsmith: 0.1.30(@langchain/core@0.1.63)(langchain@0.1.37)(openai@4.47.3)
mammoth: 1.7.2
ml-distance: 4.0.1
openapi-types: 12.1.3
p-retry: 4.6.2
pdf-parse: 1.1.1
- puppeteer: 22.6.3(typescript@5.4.2)
- redis: 4.6.13
+ puppeteer: 22.10.0(typescript@5.4.5)
+ redis: 4.6.14
uuid: 9.0.1
- yaml: 2.4.1
- zod: 3.23.4
- zod-to-json-schema: 3.23.0(zod@3.23.4)
+ yaml: 2.4.3
+ zod: 3.23.8
+ zod-to-json-schema: 3.23.0(zod@3.23.8)
transitivePeerDependencies:
- '@aws-crypto/sha256-js'
- '@aws-sdk/client-bedrock-agent-runtime'
@@ -5559,9 +5705,12 @@ packages:
- '@getzep/zep-js'
- '@gradientai/nodejs-sdk'
- '@huggingface/inference'
+ - '@mlc-ai/web-llm'
- '@mozilla/readability'
+ - '@neondatabase/serverless'
- '@opensearch-project/opensearch'
- '@planetscale/database'
+ - '@premai/prem-sdk'
- '@qdrant/js-client-rest'
- '@raycast/api'
- '@rockset/client'
@@ -5581,21 +5730,27 @@ packages:
- '@zilliz/milvus2-sdk-node'
- better-sqlite3
- cassandra-driver
+ - cborg
- closevector-common
- closevector-node
- closevector-web
- cohere-ai
- discord.js
- dria
+ - duck-duck-scrape
- encoding
- firebase-admin
- googleapis
- hnswlib-node
+ - interface-datastore
+ - it-all
+ - jsonwebtoken
- llmonitor
- lodash
- lunary
- mysql2
- neo4j-driver
+ - openai
- pg
- pg-copy-streams
- pickleparser
@@ -5607,15 +5762,29 @@ packages:
- voy-search
dev: false
- /langchainhub@0.0.8:
- resolution: {integrity: sha512-Woyb8YDHgqqTOZvWIbm2CaFDGfZ4NTSyXV687AG4vXEfoNo7cGQp7nhl7wL3ehenKWmNEmcxCLgOZzW8jE6lOQ==}
+ /langchainhub@0.0.11:
+ resolution: {integrity: sha512-WnKI4g9kU2bHQP136orXr2bcRdgz9iiTBpTN0jWt9IlScUKnJBoD0aa2HOzHURQKeQDnt2JwqVmQ6Depf5uDLQ==}
dev: false
- /langsmith@0.1.13:
- resolution: {integrity: sha512-iyGrsaWhZ70F1aG8T8Nd4iH33Z0JFMdxbfBbaRV/+LkJDH4PByZHNJbApT6G2pQmmYD0cei9oW7kXp89N5SXXQ==}
+ /langsmith@0.1.30(@langchain/core@0.1.63)(langchain@0.1.37)(openai@4.47.3):
+ resolution: {integrity: sha512-g8f10H1iiRjCweXJjgM3Y9xl6ApCa1OThDvc0BlSDLVrGVPy1on9wT39vAzYkeadC7oG48p7gfpGlYH3kLkJ9Q==}
+ peerDependencies:
+ '@langchain/core': '*'
+ langchain: '*'
+ openai: '*'
+ peerDependenciesMeta:
+ '@langchain/core':
+ optional: true
+ langchain:
+ optional: true
+ openai:
+ optional: true
dependencies:
+ '@langchain/core': 0.1.63(langchain@0.1.37)(openai@4.47.3)
'@types/uuid': 9.0.8
commander: 10.0.1
+ langchain: 0.1.37(@supabase/supabase-js@2.43.4)(axios@1.7.2)(cheerio@1.0.0-rc.12)(ioredis@5.4.1)(mammoth@1.7.2)(openai@4.47.3)(pdf-parse@1.1.1)(pg@8.11.5)(puppeteer@22.10.0)(redis@4.6.14)(typesense@1.8.2)
+ openai: 4.47.3
p-queue: 6.6.2
p-retry: 4.6.2
uuid: 9.0.1
@@ -5626,6 +5795,10 @@ packages:
engines: {node: '>= 0.4.8'}
dev: false
+ /leac@0.6.0:
+ resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
+ dev: false
+
/leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@@ -5639,6 +5812,12 @@ packages:
type-check: 0.3.2
dev: false
+ /lie@3.1.1:
+ resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
+ dependencies:
+ immediate: 3.0.6
+ dev: false
+
/lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
dependencies:
@@ -5647,6 +5826,12 @@ packages:
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+ /localforage@1.10.0:
+ resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
+ dependencies:
+ lie: 3.1.1
+ dev: false
+
/locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -5716,6 +5901,13 @@ packages:
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
dev: false
+ /loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+ dependencies:
+ js-tokens: 4.0.0
+ dev: false
+
/lop@0.4.1:
resolution: {integrity: sha512-9xyho9why2A2tzm5aIcMWKvzqKsnxrf9B5I+8O30olh6lQU8PH978LqZoI4++37RBgS1Em5i54v1TFs/3wnmXQ==}
dependencies:
@@ -5723,10 +5915,9 @@ packages:
option: 0.2.4
underscore: 1.13.6
- /lru-cache@10.2.0:
- resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
+ /lru-cache@10.2.2:
+ resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
engines: {node: 14 || >=16.14}
- dev: false
/lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -5739,6 +5930,7 @@ packages:
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
+ dev: false
/lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
@@ -5753,7 +5945,7 @@ packages:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
dependencies:
- semver: 7.6.0
+ semver: 7.6.2
dev: true
/make-error@1.3.6:
@@ -5794,6 +5986,11 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
+ /memjs@1.3.2:
+ resolution: {integrity: sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
dev: false
@@ -5809,11 +6006,11 @@ packages:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
- /micromatch@4.0.5:
- resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+ /micromatch@4.0.7:
+ resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
engines: {node: '>=8.6'}
dependencies:
- braces: 3.0.2
+ braces: 3.0.3
picomatch: 2.3.1
dev: true
@@ -5854,48 +6051,41 @@ packages:
dependencies:
brace-expansion: 2.0.1
- /minimatch@9.0.4:
- resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
+ /minimatch@9.0.1:
+ resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: false
+ /minimatch@9.0.4:
+ resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ brace-expansion: 2.0.1
+
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: false
- /minipass@3.3.6:
- resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
- engines: {node: '>=8'}
- dependencies:
- yallist: 4.0.0
- dev: true
-
- /minipass@5.0.0:
- resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
- engines: {node: '>=8'}
- dev: true
-
- /minipass@7.0.4:
- resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+ /minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
- dev: false
- /minizlib@2.1.2:
- resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
- engines: {node: '>= 8'}
+ /minizlib@3.0.1:
+ resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
+ engines: {node: '>= 18'}
dependencies:
- minipass: 3.3.6
- yallist: 4.0.0
+ minipass: 7.1.2
+ rimraf: 5.0.7
dev: true
/mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
dev: false
- /mkdirp@1.0.4:
- resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ /mkdirp@3.0.1:
+ resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
engines: {node: '>=10'}
hasBin: true
dev: true
@@ -5939,15 +6129,15 @@ packages:
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
dev: false
- /mongodb-connection-string-url@3.0.0:
- resolution: {integrity: sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==}
+ /mongodb-connection-string-url@3.0.1:
+ resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==}
dependencies:
- '@types/whatwg-url': 11.0.4
+ '@types/whatwg-url': 11.0.5
whatwg-url: 13.0.0
dev: false
- /mongodb@6.3.0:
- resolution: {integrity: sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==}
+ /mongodb@6.6.2:
+ resolution: {integrity: sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==}
engines: {node: '>=16.20.1'}
peerDependencies:
'@aws-sdk/credential-providers': ^3.188.0
@@ -5973,22 +6163,22 @@ packages:
socks:
optional: true
dependencies:
- '@mongodb-js/saslprep': 1.1.5
- bson: 6.4.0
- mongodb-connection-string-url: 3.0.0
+ '@mongodb-js/saslprep': 1.1.7
+ bson: 6.7.0
+ mongodb-connection-string-url: 3.0.1
dev: false
- /mongoose@8.2.1:
- resolution: {integrity: sha512-UgZZbXSJH0pdU936qj3FyVI+sBsMoGowFnL5R/RYrA50ayn6+ZYdVr8ehsRgNxRcMYwoNld5XzHIfkFRJTePEw==}
+ /mongoose@8.4.1:
+ resolution: {integrity: sha512-odQ2WEWGL3hb0Qex+QMN4eH6D34WdMEw7F1If2MGABApSDmG9cMmqv/G1H6WsXmuaH9mkuuadW/WbLE5+tHJwA==}
engines: {node: '>=16.20.1'}
dependencies:
- bson: 6.4.0
- kareem: 2.5.1
- mongodb: 6.3.0
+ bson: 6.7.0
+ kareem: 2.6.3
+ mongodb: 6.6.2
mpath: 0.9.0
mquery: 5.0.0
ms: 2.1.3
- sift: 16.0.1
+ sift: 17.1.3
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
- '@mongodb-js/zstd'
@@ -6009,7 +6199,7 @@ packages:
resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==}
engines: {node: '>=14.0.0'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
dev: false
@@ -6038,11 +6228,16 @@ packages:
'@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2
optional: true
- /msgpackr@1.10.1:
- resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
+ /msgpackr@1.10.2:
+ resolution: {integrity: sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==}
optionalDependencies:
msgpackr-extract: 3.0.2
+ /mustache@4.2.0:
+ resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
+ hasBin: true
+ dev: false
+
/nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -6053,18 +6248,34 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
- /natural@6.10.5:
- resolution: {integrity: sha512-YN35yqEXQu96bQkEJ1Xokyw1i/Ei04689COF3Pt/87dGpCmI3Q9bJ+YARQ+3L27/H55ofEx0evZWE/HPS4sYVw==}
+ /natural@6.12.0:
+ resolution: {integrity: sha512-ZV/cuaxOvJ7CSxQRYHc6nlx7ql6hVPQc20N5ubdqVbotWnnqsNc+0/QG+ACIC3XPQ4rfrQrdC/1k47v1cSszTQ==}
engines: {node: '>=0.4.10'}
dependencies:
afinn-165: 1.0.4
afinn-165-financialmarketnews: 3.0.0
apparatus: 0.0.10
+ dotenv: 16.4.5
+ memjs: 1.3.2
+ mongoose: 8.4.1
+ pg: 8.11.5
+ redis: 4.6.14
safe-stable-stringify: 2.4.3
stopwords-iso: 1.1.0
sylvester: 0.0.12
underscore: 1.13.6
+ uuid: 9.0.1
wordnet-db: 3.1.14
+ transitivePeerDependencies:
+ - '@aws-sdk/credential-providers'
+ - '@mongodb-js/zstd'
+ - gcp-metadata
+ - kerberos
+ - mongodb-client-encryption
+ - pg-native
+ - snappy
+ - socks
+ - supports-color
dev: false
/negotiator@0.6.3:
@@ -6135,16 +6346,17 @@ packages:
semver: 5.7.2
simple-update-notifier: 1.1.0
supports-color: 5.5.0
- touch: 3.1.0
+ touch: 3.1.1
undefsafe: 2.0.5
dev: true
- /nopt@1.0.10:
- resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==}
+ /nopt@7.2.1:
+ resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
hasBin: true
dependencies:
- abbrev: 1.1.1
- dev: true
+ abbrev: 2.0.0
+ dev: false
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
@@ -6209,15 +6421,14 @@ packages:
- debug
dev: false
- /openai@4.28.4:
- resolution: {integrity: sha512-RNIwx4MT/F0zyizGcwS+bXKLzJ8QE9IOyigDG/ttnwB220d58bYjYFp0qjvGwEFBO6+pvFVIDABZPGDl46RFsg==}
+ /openai@4.47.3:
+ resolution: {integrity: sha512-470d4ibH5kizXflCzgur22GpM4nOjrg7WQ9jTOa3dNKEn248oBy4+pjOyfcFR4V4YUn/YlDNjp6h83PbviCCKQ==}
hasBin: true
dependencies:
- '@types/node': 18.19.22
+ '@types/node': 18.19.34
'@types/node-fetch': 2.6.11
abort-controller: 3.0.0
agentkeepalive: 4.5.0
- digest-fetch: 1.3.0
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
@@ -6304,7 +6515,7 @@ packages:
engines: {node: '>= 14'}
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
- agent-base: 7.1.0
+ agent-base: 7.1.1
debug: 4.3.4
get-uri: 6.0.3
http-proxy-agent: 7.0.2
@@ -6337,7 +6548,7 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
dependencies:
- '@babel/code-frame': 7.23.5
+ '@babel/code-frame': 7.24.6
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
@@ -6359,6 +6570,13 @@ packages:
entities: 4.5.0
dev: false
+ /parseley@0.12.1:
+ resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
+ dependencies:
+ leac: 0.6.0
+ peberminta: 0.9.0
+ dev: false
+
/parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
@@ -6379,13 +6597,12 @@ packages:
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
- /path-scurry@1.10.2:
- resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
- engines: {node: '>=16 || 14 >=14.17'}
+ /path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
dependencies:
- lru-cache: 10.2.0
- minipass: 7.0.4
- dev: false
+ lru-cache: 10.2.2
+ minipass: 7.1.2
/path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
@@ -6400,15 +6617,37 @@ packages:
- supports-color
dev: false
+ /peberminta@0.9.0:
+ resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
+ dev: false
+
/pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: false
+ /pg-cloudflare@1.1.1:
+ resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /pg-connection-string@2.6.4:
+ resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
+ dev: false
+
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: false
+ /pg-pool@3.6.2(pg@8.11.5):
+ resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==}
+ peerDependencies:
+ pg: '>=8.0'
+ dependencies:
+ pg: 8.11.5
+ dev: false
+
/pg-protocol@1.6.1:
resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
dev: false
@@ -6424,8 +6663,32 @@ packages:
postgres-interval: 1.2.0
dev: false
- /picocolors@1.0.0:
- resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+ /pg@8.11.5:
+ resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==}
+ engines: {node: '>= 8.0.0'}
+ peerDependencies:
+ pg-native: '>=3.0.1'
+ peerDependenciesMeta:
+ pg-native:
+ optional: true
+ dependencies:
+ pg-connection-string: 2.6.4
+ pg-pool: 3.6.2(pg@8.11.5)
+ pg-protocol: 1.6.1
+ pg-types: 2.2.0
+ pgpass: 1.0.5
+ optionalDependencies:
+ pg-cloudflare: 1.1.1
+ dev: false
+
+ /pgpass@1.0.5:
+ resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
+ dependencies:
+ split2: 4.2.0
+ dev: false
+
+ /picocolors@1.0.1:
+ resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@@ -6456,13 +6719,13 @@ packages:
engines: {node: '>=0'}
dev: false
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
- picocolors: 1.0.0
- source-map-js: 1.0.2
+ picocolors: 1.0.1
+ source-map-js: 1.2.0
dev: false
/postgres-array@2.0.0:
@@ -6491,7 +6754,7 @@ packages:
resolution: {integrity: sha512-rtqm2h22QxLGBrW2bLYzbRhliIrqgZ0k+gF0LkQ1SNdeD06YE5eilV0MxZppFSxC8TfH0+B0cWCuebEnreIDgQ==}
engines: {node: '>=15.0.0'}
dependencies:
- axios: 1.6.7
+ axios: 1.7.2
rusha: 0.8.14
transitivePeerDependencies:
- debug
@@ -6508,7 +6771,7 @@ packages:
dependencies:
'@jest/schemas': 29.6.3
ansi-styles: 5.2.0
- react-is: 18.2.0
+ react-is: 18.3.1
dev: true
/process-nextick-args@2.0.1:
@@ -6531,16 +6794,16 @@ packages:
/promptable@0.0.9:
resolution: {integrity: sha512-qLPH4CQ+/gCAzspDdjyW3danTVUeJFnyJfvwQDD0/H5UwY2KGmNs3R61Z1jwgEn/JAOB/btep/hmoH6tqh3LAw==}
dependencies:
- axios: 1.6.7
+ axios: 1.7.2
chalk: 4.1.2
- csv-parse: 5.5.5
+ csv-parse: 5.5.6
gpt3-tokenizer: 1.1.5
handlebars: 4.7.8
openai: 3.3.0
sbd: 1.0.19
typescript: 5.4.5
uuid: 9.0.1
- zod: 3.23.4
+ zod: 3.23.8
transitivePeerDependencies:
- debug
dev: false
@@ -6553,6 +6816,10 @@ packages:
sisteransi: 1.0.5
dev: true
+ /proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+ dev: false
+
/protobufjs@7.3.0:
resolution: {integrity: sha512-YWD03n3shzV9ImZRX3ccbjqLxj7NokGN0V/ESiBV5xWqrommYHYiihuIyavq03pWSGqlyvYUFmfoMKd+1rPA/g==}
engines: {node: '>=12.0.0'}
@@ -6568,7 +6835,7 @@ packages:
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
long: 5.2.3
dev: false
@@ -6583,7 +6850,7 @@ packages:
resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==}
engines: {node: '>= 14'}
dependencies:
- agent-base: 7.1.0
+ agent-base: 7.1.1
debug: 4.3.4
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
@@ -6615,31 +6882,31 @@ packages:
engines: {node: '>=6'}
dev: false
- /puppeteer-core@22.6.3:
- resolution: {integrity: sha512-YrTAak5zCTWVTnVaCK1b7FD1qFCCT9bSvQhLzamnIsJ57/tfuXiT8ZvPJR2SBfahyFTYFCcaZAd/Npow3lmDGA==}
+ /puppeteer-core@22.10.0:
+ resolution: {integrity: sha512-I54J4Vy4I07UHsgB1QSmuFoF7KNQjJWcvFBPhtY+ezMdBfwgGDr8dzYrJa11aPgP9kxIUHjhktcMmmfJkOAtTw==}
engines: {node: '>=18'}
dependencies:
- '@puppeteer/browsers': 2.2.1
- chromium-bidi: 0.5.16(devtools-protocol@0.0.1262051)
+ '@puppeteer/browsers': 2.2.3
+ chromium-bidi: 0.5.19(devtools-protocol@0.0.1286932)
debug: 4.3.4
- devtools-protocol: 0.0.1262051
- ws: 8.16.0
+ devtools-protocol: 0.0.1286932
+ ws: 8.17.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false
- /puppeteer@22.6.3(typescript@5.4.2):
- resolution: {integrity: sha512-KZOnthMbvr4+7cPKFeeOCJPe8DzOKGC0GbMXmZKOP/YusXQ6sqxA0vt6frstZq3rUJ277qXHlZNFf01VVwdHKw==}
+ /puppeteer@22.10.0(typescript@5.4.5):
+ resolution: {integrity: sha512-ZOkZd6a6t0BdKcWb0wAYHWQqCfdlN1PPnXOmg/XNrbo6gJhYWFX4qCNb6ahSn8TpAqBqLCoD4Q010F7GwOM7mA==}
engines: {node: '>=18'}
hasBin: true
requiresBuild: true
dependencies:
- '@puppeteer/browsers': 2.2.1
- cosmiconfig: 9.0.0(typescript@5.4.2)
- devtools-protocol: 0.0.1262051
- puppeteer-core: 22.6.3
+ '@puppeteer/browsers': 2.2.3
+ cosmiconfig: 9.0.0(typescript@5.4.5)
+ devtools-protocol: 0.0.1286932
+ puppeteer-core: 22.10.0
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -6647,8 +6914,8 @@ packages:
- utf-8-validate
dev: false
- /pure-rand@6.0.4:
- resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==}
+ /pure-rand@6.1.0:
+ resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
dev: true
/qs@6.11.0:
@@ -6657,15 +6924,14 @@ packages:
dependencies:
side-channel: 1.0.6
- /qs@6.12.0:
- resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
+ /qs@6.12.1:
+ resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.6
/queue-tick@1.0.1:
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
- requiresBuild: true
dev: false
/range-parser@1.2.1:
@@ -6685,10 +6951,27 @@ packages:
iconv-lite: 0.4.24
unpipe: 1.0.0
- /react-is@18.2.0:
- resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
+ /react-dom@18.2.0(react@18.2.0):
+ resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
+ peerDependencies:
+ react: ^18.2.0
+ dependencies:
+ loose-envify: 1.4.0
+ react: 18.2.0
+ scheduler: 0.23.2
+ dev: false
+
+ /react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
dev: true
+ /react@18.2.0:
+ resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ loose-envify: 1.4.0
+ dev: false
+
/read-cmd-shim@4.0.0:
resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -6710,7 +6993,7 @@ packages:
engines: {node: '>= 6'}
dependencies:
inherits: 2.0.4
- string_decoder: 1.1.1
+ string_decoder: 1.3.0
util-deprecate: 1.0.2
dev: false
@@ -6748,15 +7031,15 @@ packages:
dependencies:
redis-errors: 1.2.0
- /redis@4.6.13:
- resolution: {integrity: sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==}
+ /redis@4.6.14:
+ resolution: {integrity: sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==}
dependencies:
- '@redis/bloom': 1.2.0(@redis/client@1.5.14)
- '@redis/client': 1.5.14
- '@redis/graph': 1.1.1(@redis/client@1.5.14)
- '@redis/json': 1.0.6(@redis/client@1.5.14)
- '@redis/search': 1.1.6(@redis/client@1.5.14)
- '@redis/time-series': 1.0.5(@redis/client@1.5.14)
+ '@redis/bloom': 1.2.0(@redis/client@1.5.16)
+ '@redis/client': 1.5.16
+ '@redis/graph': 1.1.1(@redis/client@1.5.16)
+ '@redis/json': 1.0.6(@redis/client@1.5.16)
+ '@redis/search': 1.1.6(@redis/client@1.5.16)
+ '@redis/time-series': 1.0.5(@redis/client@1.5.16)
dev: false
/reflect-metadata@0.1.14:
@@ -6780,13 +7063,20 @@ packages:
resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==}
engines: {node: '>=8.6.0'}
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
module-details-from-path: 1.0.3
resolve: 1.22.8
transitivePeerDependencies:
- supports-color
dev: false
+ /resend@3.2.0:
+ resolution: {integrity: sha512-lDHhexiFYPoLXy7zRlJ8D5eKxoXy6Tr9/elN3+Vv7PkUoYuSSD1fpiIfa/JYXEWyiyN2UczkCTLpkT8dDPJ4Pg==}
+ engines: {node: '>=18'}
+ dependencies:
+ '@react-email/render': 0.0.12
+ dev: false
+
/resolve-cwd@3.0.0:
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
engines: {node: '>=8'}
@@ -6822,6 +7112,14 @@ packages:
engines: {node: '>= 4'}
dev: false
+ /rimraf@5.0.7:
+ resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==}
+ engines: {node: '>=14.18'}
+ hasBin: true
+ dependencies:
+ glob: 10.4.1
+ dev: true
+
/robots-parser@3.0.1:
resolution: {integrity: sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==}
engines: {node: '>=10.0.0'}
@@ -6845,37 +7143,49 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- /sanitize-html@2.12.1:
- resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==}
+ /sanitize-html@2.13.0:
+ resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==}
dependencies:
deepmerge: 4.3.1
escape-string-regexp: 4.0.0
htmlparser2: 8.0.2
is-plain-object: 5.0.0
parse-srcset: 1.0.2
- postcss: 8.4.35
+ postcss: 8.4.38
dev: false
- /sax@1.3.0:
- resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
+ /sax@1.4.1:
+ resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
dev: false
/sbd@1.0.19:
resolution: {integrity: sha512-b5RyZMGSrFuIB4AHdbv12uYHS8YGEJ36gtuvG3RflbJGY+T0dXmAL0E4vZjQqT2RsX0v+ZwVqhV2zsGr5aFK9w==}
dependencies:
- sanitize-html: 2.12.1
+ sanitize-html: 2.13.0
+ dev: false
+
+ /scheduler@0.23.2:
+ resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
+ dependencies:
+ loose-envify: 1.4.0
dev: false
/scrapingbee@1.7.4:
resolution: {integrity: sha512-cTo+mfLi+T3mSeCHIefVZpjWEX2O70SkmCoWj9ypsnIFqBI2GmljdHYXt8yoT6D/YKjI0rHE7YH9iVRdhyoMmQ==}
dependencies:
- axios: 1.6.7
+ axios: 1.7.2
axios-retry: 3.9.1
yargs-unparser: 2.0.0
transitivePeerDependencies:
- debug
dev: false
+ /selderee@0.11.0:
+ resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
+ dependencies:
+ parseley: 0.12.1
+ dev: false
+
/semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@@ -6897,6 +7207,12 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
+ dev: false
+
+ /semver@7.6.2:
+ resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
+ engines: {node: '>=10'}
+ hasBin: true
/send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@@ -6936,8 +7252,8 @@ packages:
transitivePeerDependencies:
- supports-color
- /set-function-length@1.2.1:
- resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
+ /set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
dependencies:
define-data-property: 1.1.4
@@ -6980,8 +7296,8 @@ packages:
get-intrinsic: 1.2.4
object-inspect: 1.13.1
- /sift@16.0.1:
- resolution: {integrity: sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==}
+ /sift@17.1.3:
+ resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==}
dev: false
/signal-exit@3.0.7:
@@ -7032,8 +7348,8 @@ packages:
smart-buffer: 4.2.0
dev: false
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: false
@@ -7095,13 +7411,14 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
- /streamx@2.16.1:
- resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==}
+ /streamx@2.18.0:
+ resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==}
dependencies:
fast-fifo: 1.3.2
queue-tick: 1.0.1
+ text-decoder: 1.1.0
optionalDependencies:
- bare-events: 2.2.2
+ bare-events: 2.3.1
dev: false
/string-length@4.0.2:
@@ -7127,7 +7444,6 @@ packages:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
- dev: false
/string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
@@ -7151,7 +7467,6 @@ packages:
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
- dev: false
/strip-bom@4.0.0:
resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
@@ -7172,20 +7487,20 @@ packages:
resolution: {integrity: sha512-cYjgBM2SY/dTm8Lr6eMyyONaHTZHA/QjHxFUIW5WH8FevSRIGAVtXEmBkUXF1fsqe7QvvRgQSGSJZmjDacegGg==}
engines: {node: '>=12.*'}
dependencies:
- '@types/node': 20.11.25
- qs: 6.12.0
+ '@types/node': 20.14.1
+ qs: 6.12.1
dev: false
- /supabase@1.148.6:
- resolution: {integrity: sha512-/VXbN0C/r/o1TkHGw4EXYvwtwfsSvRaBd6lWa5HamcC6HdFpbof8VsSglWjiie5ONTg3lwqv06wcOrtdpzz/Bg==}
+ /supabase@1.172.2:
+ resolution: {integrity: sha512-h2J6kKEikXnZyurUcCYg215qkQpINOhdWkiclHcWAuVeqXsNrfrYaf1s0qbbcdRyMtrVW48I+VdVTw71Cnn20Q==}
engines: {npm: '>=8'}
hasBin: true
requiresBuild: true
dependencies:
- bin-links: 4.0.3
+ bin-links: 4.0.4
https-proxy-agent: 7.0.4
node-fetch: 3.3.2
- tar: 6.2.0
+ tar: 7.2.0
transitivePeerDependencies:
- supports-color
dev: true
@@ -7193,17 +7508,18 @@ packages:
/superagent@8.1.2:
resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==}
engines: {node: '>=6.4.0 <13 || >=14'}
+ deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
- debug: 4.3.4
+ debug: 4.3.5
fast-safe-stringify: 2.1.1
form-data: 4.0.0
formidable: 2.1.2
methods: 1.1.2
mime: 2.6.0
- qs: 6.12.0
- semver: 7.6.0
+ qs: 6.12.1
+ semver: 7.6.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -7252,8 +7568,8 @@ packages:
pump: 3.0.0
tar-stream: 3.1.7
optionalDependencies:
- bare-fs: 2.2.3
- bare-path: 2.1.1
+ bare-fs: 2.3.1
+ bare-path: 2.1.3
dev: false
/tar-stream@3.1.7:
@@ -7261,19 +7577,19 @@ packages:
dependencies:
b4a: 1.6.6
fast-fifo: 1.3.2
- streamx: 2.16.1
+ streamx: 2.18.0
dev: false
- /tar@6.2.0:
- resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
- engines: {node: '>=10'}
+ /tar@7.2.0:
+ resolution: {integrity: sha512-hctwP0Nb4AB60bj8WQgRYaMOuJYRAPMGiQUAotms5igN8ppfQM+IvjQ5HcKu1MaZh2Wy2KWVTe563Yj8dfc14w==}
+ engines: {node: '>=18'}
dependencies:
- chownr: 2.0.0
- fs-minipass: 2.1.0
- minipass: 5.0.0
- minizlib: 2.1.2
- mkdirp: 1.0.4
- yallist: 4.0.0
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.0.1
+ mkdirp: 3.0.1
+ yallist: 5.0.0
dev: true
/test-exclude@6.0.0:
@@ -7285,6 +7601,12 @@ packages:
minimatch: 3.1.2
dev: true
+ /text-decoder@1.1.0:
+ resolution: {integrity: sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==}
+ dependencies:
+ b4a: 1.6.6
+ dev: false
+
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
dev: false
@@ -7309,11 +7631,9 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
- /touch@3.1.0:
- resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
+ /touch@3.1.1:
+ resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
hasBin: true
- dependencies:
- nopt: 1.0.10
dev: true
/tr46@0.0.3:
@@ -7331,12 +7651,13 @@ packages:
engines: {node: '>= 14.0.0'}
dev: false
- /ts-jest@29.1.2(@babel/core@7.24.0)(jest@29.7.0)(typescript@5.4.2):
- resolution: {integrity: sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==}
- engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0}
+ /ts-jest@29.1.4(@babel/core@7.24.6)(jest@29.7.0)(typescript@5.4.5):
+ resolution: {integrity: sha512-YiHwDhSvCiItoAgsKtoLFCuakDzDsJ1DLDnSouTaTmdOcOwIkSzbLXduaQ6M5DRVhuZC/NYaaZ/mtHbWMv/S6Q==}
+ 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: '*'
@@ -7345,6 +7666,8 @@ packages:
peerDependenciesMeta:
'@babel/core':
optional: true
+ '@jest/transform':
+ optional: true
'@jest/types':
optional: true
babel-jest:
@@ -7352,20 +7675,20 @@ packages:
esbuild:
optional: true
dependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.24.6
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
- jest: 29.7.0(@types/node@20.11.25)(ts-node@10.9.2)
+ jest: 29.7.0(@types/node@20.14.1)(ts-node@10.9.2)
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
- semver: 7.6.0
- typescript: 5.4.2
+ semver: 7.6.2
+ typescript: 5.4.5
yargs-parser: 21.1.1
dev: true
- /ts-node@10.9.2(@types/node@20.11.25)(typescript@5.4.2):
+ /ts-node@10.9.2(@types/node@20.14.1)(typescript@5.4.5):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
@@ -7380,18 +7703,18 @@ packages:
optional: true
dependencies:
'@cspotcode/source-map-support': 0.8.1
- '@tsconfig/node10': 1.0.9
+ '@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 20.11.25
+ '@types/node': 20.14.1
acorn: 8.11.3
acorn-walk: 8.3.2
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
- typescript: 5.4.2
+ typescript: 5.4.5
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
@@ -7404,10 +7727,10 @@ packages:
resolution: {integrity: sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==}
dev: false
- /turndown@7.1.3:
- resolution: {integrity: sha512-Z3/iJ6IWh8VBiACWQJaA5ulPQE5E1QwvBHj00uGzdQxdRnd8fh1DPqNOJqzQDu6DkOstORrtXzf/9adB+vMtEA==}
+ /turndown@7.2.0:
+ resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==}
dependencies:
- domino: 2.1.6
+ '@mixmark-io/domino': 2.2.0
dev: false
/type-check@0.3.2:
@@ -7439,25 +7762,19 @@ packages:
media-typer: 0.3.0
mime-types: 2.1.35
- /typescript@5.4.2:
- resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
- engines: {node: '>=14.17'}
- hasBin: true
-
/typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'}
hasBin: true
- dev: false
- /typesense@1.7.2(@babel/runtime@7.24.0):
- resolution: {integrity: sha512-hgQESOiyNJq+w2mpRJa/a1UMhWtJ/+sb0p7NoeCDSkikm9sasisJdnc7uhQchM6vTWKw2sMLWUBNbAhItR6zUQ==}
- engines: {node: '>=14'}
+ /typesense@1.8.2(@babel/runtime@7.24.6):
+ resolution: {integrity: sha512-aBpePjA99Qvo+OP2pJwMpvga4Jrm1Y2oV5NsrWXBxlqUDNEUCPZBIksPv2Hq0jxQxHhLLyJVbjXjByXsvpCDVA==}
+ engines: {node: '>=18'}
peerDependencies:
- '@babel/runtime': ^7.17.2
+ '@babel/runtime': ^7.23.2
dependencies:
- '@babel/runtime': 7.24.0
- axios: 0.26.1
+ '@babel/runtime': 7.24.6
+ axios: 1.7.2
loglevel: 1.9.1
transitivePeerDependencies:
- debug
@@ -7504,7 +7821,7 @@ packages:
/unstructured-client@0.9.4:
resolution: {integrity: sha512-RDCfjvdFrQtNoRtH0KLi0Rnqc3EL5+FmuNNQsdpz/NJioFzketyelxAd8xf+v/7A0eNrWk4vxgZ8YtW4SgwV+Q==}
dependencies:
- axios: 1.6.7
+ axios: 1.7.2
class-transformer: 0.5.1
form-data: 4.0.0
jsonpath: 1.1.1
@@ -7513,23 +7830,17 @@ packages:
- debug
dev: false
- /update-browserslist-db@1.0.13(browserslist@4.23.0):
- resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+ /update-browserslist-db@1.0.16(browserslist@4.23.0):
+ resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.23.0
escalade: 3.1.2
- picocolors: 1.0.0
+ picocolors: 1.0.1
dev: true
- /uri-js@4.4.1:
- resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- dependencies:
- punycode: 2.3.1
- dev: false
-
/urlpattern-polyfill@10.0.0:
resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==}
dev: false
@@ -7664,7 +7975,6 @@ packages:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
- dev: false
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@@ -7685,8 +7995,8 @@ packages:
signal-exit: 4.1.0
dev: true
- /ws@8.16.0:
- resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
+ /ws@8.17.0:
+ resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -7702,7 +8012,7 @@ packages:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'}
dependencies:
- sax: 1.3.0
+ sax: 1.4.1
xmlbuilder: 11.0.1
dev: false
@@ -7730,9 +8040,15 @@ packages:
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ dev: false
- /yaml@2.4.1:
- resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==}
+ /yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+ dev: true
+
+ /yaml@2.4.3:
+ resolution: {integrity: sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==}
engines: {node: '>= 14'}
hasBin: true
dev: false
@@ -7780,18 +8096,18 @@ packages:
engines: {node: '>=10'}
dev: true
- /zod-to-json-schema@3.23.0(zod@3.23.4):
+ /zod-to-json-schema@3.23.0(zod@3.23.8):
resolution: {integrity: sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==}
peerDependencies:
zod: ^3.23.3
dependencies:
- zod: 3.23.4
+ zod: 3.23.8
dev: false
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
dev: false
- /zod@3.23.4:
- resolution: {integrity: sha512-/AtWOKbBgjzEYYQRNfoGKHObgfAZag6qUJX1VbHo2PRBgS+wfWagEY2mizjfyAPcGesrJOcx/wcl0L9WnVrHFw==}
+ /zod@3.23.8:
+ resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
dev: false
diff --git a/apps/api/src/__tests__/e2e_noAuth/index.test.ts b/apps/api/src/__tests__/e2e_noAuth/index.test.ts
index c443e71..acb2278 100644
--- a/apps/api/src/__tests__/e2e_noAuth/index.test.ts
+++ b/apps/api/src/__tests__/e2e_noAuth/index.test.ts
@@ -1,5 +1,4 @@
import request from "supertest";
-import { app } from "../../index";
import dotenv from "dotenv";
const fs = require("fs");
const path = require("path");
diff --git a/apps/api/src/__tests__/e2e_withAuth/index.test.ts b/apps/api/src/__tests__/e2e_withAuth/index.test.ts
index 331283e..7c234ef 100644
--- a/apps/api/src/__tests__/e2e_withAuth/index.test.ts
+++ b/apps/api/src/__tests__/e2e_withAuth/index.test.ts
@@ -1,6 +1,6 @@
import request from "supertest";
-import { app } from "../../index";
import dotenv from "dotenv";
+import { v4 as uuidv4 } from "uuid";
dotenv.config();
@@ -16,7 +16,7 @@ describe("E2E Tests for API Routes", () => {
delete process.env.USE_DB_AUTHENTICATION;
});
describe("GET /", () => {
- it("should return Hello, world! message", async () => {
+ it.concurrent("should return Hello, world! message", async () => {
const response = await request(TEST_URL).get("/");
expect(response.statusCode).toBe(200);
@@ -25,7 +25,7 @@ describe("E2E Tests for API Routes", () => {
});
describe("GET /test", () => {
- it("should return Hello, world! message", async () => {
+ it.concurrent("should return Hello, world! message", async () => {
const response = await request(TEST_URL).get("/test");
expect(response.statusCode).toBe(200);
expect(response.text).toContain("Hello, world!");
@@ -33,12 +33,12 @@ describe("E2E Tests for API Routes", () => {
});
describe("POST /v0/scrape", () => {
- it("should require authorization", async () => {
- const response = await request(app).post("/v0/scrape");
+ it.concurrent("should require authorization", async () => {
+ const response = await request(TEST_URL).post("/v0/scrape");
expect(response.statusCode).toBe(401);
});
- it("should return an error response with an invalid API key", async () => {
+ it.concurrent("should return an error response with an invalid API key", async () => {
const response = await request(TEST_URL)
.post("/v0/scrape")
.set("Authorization", `Bearer invalid-api-key`)
@@ -47,7 +47,7 @@ describe("E2E Tests for API Routes", () => {
expect(response.statusCode).toBe(401);
});
- it("should return an error for a blocklisted URL", async () => {
+ it.concurrent("should return an error for a blocklisted URL", async () => {
const blocklistedUrl = "https://facebook.com/fake-test";
const response = await request(TEST_URL)
.post("/v0/scrape")
@@ -60,37 +60,40 @@ describe("E2E Tests for API Routes", () => {
);
});
- it("should return a successful response with a valid preview token", async () => {
- const response = await request(TEST_URL)
- .post("/v0/scrape")
- .set("Authorization", `Bearer this_is_just_a_preview_token`)
- .set("Content-Type", "application/json")
- .send({ url: "https://firecrawl.dev" });
- expect(response.statusCode).toBe(200);
- }, 10000); // 10 seconds timeout
+ // tested on rate limit test
+ // it.concurrent("should return a successful response with a valid preview token", async () => {
+ // const response = await request(TEST_URL)
+ // .post("/v0/scrape")
+ // .set("Authorization", `Bearer this_is_just_a_preview_token`)
+ // .set("Content-Type", "application/json")
+ // .send({ url: "https://roastmywebsite.ai" });
+ // expect(response.statusCode).toBe(200);
+ // }, 30000); // 30 seconds timeout
- it("should return a successful response with a valid API key", async () => {
+ it.concurrent("should return a successful response with a valid API key", async () => {
const response = await request(TEST_URL)
.post("/v0/scrape")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
- .send({ url: "https://firecrawl.dev" });
+ .send({ url: "https://roastmywebsite.ai" });
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("data");
expect(response.body.data).toHaveProperty("content");
expect(response.body.data).toHaveProperty("markdown");
expect(response.body.data).toHaveProperty("metadata");
expect(response.body.data).not.toHaveProperty("html");
- expect(response.body.data.content).toContain("🔥 Firecrawl");
+ expect(response.body.data.content).toContain("_Roast_");
+ expect(response.body.data.metadata.pageStatusCode).toBe(200);
+ expect(response.body.data.metadata.pageError).toBeUndefined();
}, 30000); // 30 seconds timeout
- it("should return a successful response with a valid API key and includeHtml set to true", async () => {
+ it.concurrent("should return a successful response with a valid API key and includeHtml set to true", async () => {
const response = await request(TEST_URL)
.post("/v0/scrape")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({
- url: "https://firecrawl.dev",
+ url: "https://roastmywebsite.ai",
pageOptions: { includeHtml: true },
});
expect(response.statusCode).toBe(200);
@@ -99,12 +102,14 @@ describe("E2E Tests for API Routes", () => {
expect(response.body.data).toHaveProperty("markdown");
expect(response.body.data).toHaveProperty("html");
expect(response.body.data).toHaveProperty("metadata");
- expect(response.body.data.content).toContain("🔥 Firecrawl");
- expect(response.body.data.markdown).toContain("🔥 Firecrawl");
+ expect(response.body.data.content).toContain("_Roast_");
+ expect(response.body.data.markdown).toContain("_Roast_");
expect(response.body.data.html).toContain("
{
+ it.concurrent('should return a successful response for a valid scrape with PDF file', async () => {
const response = await request(TEST_URL)
.post('/v0/scrape')
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
@@ -117,9 +122,11 @@ describe("E2E Tests for API Routes", () => {
expect(response.body.data).toHaveProperty('content');
expect(response.body.data).toHaveProperty('metadata');
expect(response.body.data.content).toContain('We present spectrophotometric observations of the Broad Line Radio Galaxy');
+ expect(response.body.data.metadata.pageStatusCode).toBe(200);
+ expect(response.body.data.metadata.pageError).toBeUndefined();
}, 60000); // 60 seconds
- it('should return a successful response for a valid scrape with PDF file without explicit .pdf extension', async () => {
+ it.concurrent('should return a successful response for a valid scrape with PDF file without explicit .pdf extension', async () => {
const response = await request(TEST_URL)
.post('/v0/scrape')
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
@@ -132,16 +139,184 @@ describe("E2E Tests for API Routes", () => {
expect(response.body.data).toHaveProperty('content');
expect(response.body.data).toHaveProperty('metadata');
expect(response.body.data.content).toContain('We present spectrophotometric observations of the Broad Line Radio Galaxy');
+ expect(response.body.data.metadata.pageStatusCode).toBe(200);
+ expect(response.body.data.metadata.pageError).toBeUndefined();
+ }, 60000); // 60 seconds
+
+ it.concurrent('should return a successful response for a valid scrape with PDF file and parsePDF set to false', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://arxiv.org/pdf/astro-ph/9301001.pdf', pageOptions: { parsePDF: false } });
+ await new Promise((r) => setTimeout(r, 6000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.content).toContain('/Title(arXiv:astro-ph/9301001v1 7 Jan 1993)>>endobj');
+ }, 60000); // 60 seconds
+
+ it.concurrent("should return a successful response with a valid API key with removeTags option", async () => {
+ const responseWithoutRemoveTags = await request(TEST_URL)
+ .post("/v0/scrape")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({ url: "https://www.scrapethissite.com/" });
+ expect(responseWithoutRemoveTags.statusCode).toBe(200);
+ expect(responseWithoutRemoveTags.body).toHaveProperty("data");
+ expect(responseWithoutRemoveTags.body.data).toHaveProperty("content");
+ expect(responseWithoutRemoveTags.body.data).toHaveProperty("markdown");
+ expect(responseWithoutRemoveTags.body.data).toHaveProperty("metadata");
+ expect(responseWithoutRemoveTags.body.data).not.toHaveProperty("html");
+ expect(responseWithoutRemoveTags.body.data.content).toContain("Scrape This Site");
+ expect(responseWithoutRemoveTags.body.data.content).toContain("Lessons and Videos"); // #footer
+ expect(responseWithoutRemoveTags.body.data.content).toContain("[Sandbox]("); // .nav
+ expect(responseWithoutRemoveTags.body.data.content).toContain("web scraping"); // strong
+
+ const response = await request(TEST_URL)
+ .post("/v0/scrape")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({ url: "https://www.scrapethissite.com/", pageOptions: { removeTags: ['.nav', '#footer', 'strong'] } });
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("data");
+ expect(response.body.data).toHaveProperty("content");
+ expect(response.body.data).toHaveProperty("markdown");
+ expect(response.body.data).toHaveProperty("metadata");
+ expect(response.body.data).not.toHaveProperty("html");
+ expect(response.body.data.content).toContain("Scrape This Site");
+ expect(response.body.data.content).not.toContain("Lessons and Videos"); // #footer
+ expect(response.body.data.content).not.toContain("[Sandbox]("); // .nav
+ expect(response.body.data.content).not.toContain("web scraping"); // strong
+ }, 30000); // 30 seconds timeout
+
+ // TODO: add this test back once we nail the waitFor option to be more deterministic
+ // it.concurrent("should return a successful response with a valid API key and waitFor option", async () => {
+ // const startTime = Date.now();
+ // const response = await request(TEST_URL)
+ // .post("/v0/scrape")
+ // .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ // .set("Content-Type", "application/json")
+ // .send({ url: "https://firecrawl.dev", pageOptions: { waitFor: 7000 } });
+ // const endTime = Date.now();
+ // const duration = endTime - startTime;
+
+ // expect(response.statusCode).toBe(200);
+ // expect(response.body).toHaveProperty("data");
+ // expect(response.body.data).toHaveProperty("content");
+ // expect(response.body.data).toHaveProperty("markdown");
+ // expect(response.body.data).toHaveProperty("metadata");
+ // expect(response.body.data).not.toHaveProperty("html");
+ // expect(response.body.data.content).toContain("🔥 Firecrawl");
+ // expect(duration).toBeGreaterThanOrEqual(7000);
+ // }, 12000); // 12 seconds timeout
+
+ it.concurrent('should return a successful response for a scrape with 400 page', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/400' });
+ await new Promise((r) => setTimeout(r, 5000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(400);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("bad request");
+ }, 60000); // 60 seconds
+
+ it.concurrent('should return a successful response for a scrape with 401 page', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/401' });
+ await new Promise((r) => setTimeout(r, 5000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(401);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("unauthorized");
+ }, 60000); // 60 seconds
+
+ it.concurrent("should return a successful response for a scrape with 403 page", async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/403' });
+
+ await new Promise((r) => setTimeout(r, 5000));
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(403);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("forbidden");
+ }, 60000); // 60 seconds
+
+ it.concurrent('should return a successful response for a scrape with 404 page', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/404' });
+ await new Promise((r) => setTimeout(r, 5000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(404);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("not found");
+ }, 60000); // 60 seconds
+
+ it.concurrent('should return a successful response for a scrape with 405 page', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/405' });
+ await new Promise((r) => setTimeout(r, 5000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(405);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("method not allowed");
+ }, 60000); // 60 seconds
+
+ it.concurrent('should return a successful response for a scrape with 500 page', async () => {
+ const response = await request(TEST_URL)
+ .post('/v0/scrape')
+ .set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
+ .set('Content-Type', 'application/json')
+ .send({ url: 'https://httpstat.us/500' });
+ await new Promise((r) => setTimeout(r, 5000));
+
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty('data');
+ expect(response.body.data).toHaveProperty('content');
+ expect(response.body.data).toHaveProperty('metadata');
+ expect(response.body.data.metadata.pageStatusCode).toBe(500);
+ expect(response.body.data.metadata.pageError.toLowerCase()).toContain("internal server error");
}, 60000); // 60 seconds
});
describe("POST /v0/crawl", () => {
- it("should require authorization", async () => {
+ it.concurrent("should require authorization", async () => {
const response = await request(TEST_URL).post("/v0/crawl");
expect(response.statusCode).toBe(401);
});
- it("should return an error response with an invalid API key", async () => {
+ it.concurrent("should return an error response with an invalid API key", async () => {
const response = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer invalid-api-key`)
@@ -150,7 +325,7 @@ describe("E2E Tests for API Routes", () => {
expect(response.statusCode).toBe(401);
});
- it("should return an error for a blocklisted URL", async () => {
+ it.concurrent("should return an error for a blocklisted URL", async () => {
const blocklistedUrl = "https://twitter.com/fake-test";
const response = await request(TEST_URL)
.post("/v0/crawl")
@@ -163,7 +338,7 @@ describe("E2E Tests for API Routes", () => {
);
});
- it("should return a successful response with a valid API key", async () => {
+ it.concurrent("should return a successful response with a valid API key for crawl", async () => {
const response = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -175,8 +350,32 @@ describe("E2E Tests for API Routes", () => {
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
);
});
+ it.concurrent('should prevent duplicate requests using the same idempotency key', async () => {
+ const uniqueIdempotencyKey = uuidv4();
+
+ // First request with the idempotency key
+ const firstResponse = await request(TEST_URL)
+ .post('/v0/crawl')
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .set("x-idempotency-key", uniqueIdempotencyKey)
+ .send({ url: 'https://mendable.ai' });
+
+ expect(firstResponse.statusCode).toBe(200);
+
+ // Second request with the same idempotency key
+ const secondResponse = await request(TEST_URL)
+ .post('/v0/crawl')
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .set("x-idempotency-key", uniqueIdempotencyKey)
+ .send({ url: 'https://mendable.ai' });
+
+ expect(secondResponse.statusCode).toBe(409);
+ expect(secondResponse.body.error).toBe('Idempotency key already used');
+ });
- it("should return a successful response with a valid API key and valid includes option", async () => {
+ it.concurrent("should return a successful response with a valid API key and valid includes option", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -213,7 +412,6 @@ describe("E2E Tests for API Routes", () => {
);
expect(urls.length).toBeGreaterThan(5);
urls.forEach((url: string) => {
- console.log({url})
expect(url.startsWith("https://www.mendable.ai/blog/")).toBeTruthy();
});
@@ -225,9 +423,11 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
expect(completedResponse.body.data[0].content).toContain("Mendable");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
}, 60000); // 60 seconds
- it("should return a successful response with a valid API key and valid excludes option", async () => {
+ it.concurrent("should return a successful response with a valid API key and valid excludes option", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -266,9 +466,9 @@ describe("E2E Tests for API Routes", () => {
urls.forEach((url: string) => {
expect(url.startsWith("https://wwww.mendable.ai/blog/")).toBeFalsy();
});
- }, 60000); // 60 seconds
+ }, 90000); // 90 seconds
- it("should return a successful response with a valid API key and limit to 3", async () => {
+ it.concurrent("should return a successful response with a valid API key and limit to 3", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -306,16 +506,18 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
expect(completedResponse.body.data[0].content).toContain("Mendable");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
}, 60000); // 60 seconds
- it("should return a successful response with max depth option for a valid crawl job", async () => {
+ it.concurrent("should return a successful response with max depth option for a valid crawl job", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({
url: "https://www.scrapethissite.com",
- crawlerOptions: { maxDepth: 2 },
+ crawlerOptions: { maxDepth: 1 },
});
expect(crawlResponse.statusCode).toBe(200);
@@ -324,9 +526,74 @@ describe("E2E Tests for API Routes", () => {
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("status");
- expect(response.body.status).toBe("active");
+ expect(["active", "waiting"]).toContain(response.body.status);
// wait for 60 seconds
- await new Promise((r) => setTimeout(r, 60000));
+ let isCompleted = false;
+ while (!isCompleted) {
+ const statusCheckResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(statusCheckResponse.statusCode).toBe(200);
+ isCompleted = statusCheckResponse.body.status === "completed";
+ if (!isCompleted) {
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second before checking again
+ }
+ }
+ const completedResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+
+ expect(completedResponse.statusCode).toBe(200);
+ expect(completedResponse.body).toHaveProperty("status");
+ expect(completedResponse.body.status).toBe("completed");
+ expect(completedResponse.body).toHaveProperty("data");
+ expect(completedResponse.body.data[0]).toHaveProperty("content");
+ expect(completedResponse.body.data[0]).toHaveProperty("markdown");
+ expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
+ const urls = completedResponse.body.data.map(
+ (item: any) => item.metadata?.sourceURL
+ );
+ expect(urls.length).toBeGreaterThan(1);
+
+ // Check if all URLs have a maximum depth of 1
+ urls.forEach((url: string) => {
+ const pathSplits = new URL(url).pathname.split('/');
+ const depth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
+ expect(depth).toBeLessThanOrEqual(2);
+ });
+ }, 180000);
+
+ it.concurrent("should return a successful response with relative max depth option for a valid crawl job", async () => {
+ const crawlResponse = await request(TEST_URL)
+ .post("/v0/crawl")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({
+ url: "https://www.scrapethissite.com/pages/",
+ crawlerOptions: { maxDepth: 1 },
+ });
+ expect(crawlResponse.statusCode).toBe(200);
+
+ const response = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("status");
+ expect(["active", "waiting"]).toContain(response.body.status);
+ // wait for 60 seconds
+ let isCompleted = false;
+ while (!isCompleted) {
+ const statusCheckResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(statusCheckResponse.statusCode).toBe(200);
+ isCompleted = statusCheckResponse.body.status === "completed";
+ if (!isCompleted) {
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second before checking again
+ }
+ }
const completedResponse = await request(TEST_URL)
.get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
@@ -343,14 +610,121 @@ describe("E2E Tests for API Routes", () => {
);
expect(urls.length).toBeGreaterThan(1);
- // Check if all URLs have a maximum depth of 1
+ // Check if all URLs have an absolute maximum depth of 3 after the base URL depth was 2 and the maxDepth was 1
urls.forEach((url: string) => {
- const depth = new URL(url).pathname.split("/").filter(Boolean).length;
+ const pathSplits = new URL(url).pathname.split('/');
+ const depth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
+ expect(depth).toBeLessThanOrEqual(3);
+ });
+ }, 180000);
+
+ it.concurrent("should return a successful response with relative max depth option for a valid crawl job with maxDepths equals to zero", async () => {
+ const crawlResponse = await request(TEST_URL)
+ .post("/v0/crawl")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({
+ url: "https://www.scrapethissite.com",
+ crawlerOptions: { maxDepth: 0 },
+ });
+ expect(crawlResponse.statusCode).toBe(200);
+
+ const response = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("status");
+ expect(["active", "waiting"]).toContain(response.body.status);
+ // wait for 60 seconds
+ let isCompleted = false;
+ while (!isCompleted) {
+ const statusCheckResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(statusCheckResponse.statusCode).toBe(200);
+ isCompleted = statusCheckResponse.body.status === "completed";
+ if (!isCompleted) {
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second before checking again
+ }
+ }
+ const completedResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+
+ expect(completedResponse.statusCode).toBe(200);
+ expect(completedResponse.body).toHaveProperty("status");
+ expect(completedResponse.body.status).toBe("completed");
+ expect(completedResponse.body).toHaveProperty("data");
+ expect(completedResponse.body.data[0]).toHaveProperty("content");
+ expect(completedResponse.body.data[0]).toHaveProperty("markdown");
+ expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ const urls = completedResponse.body.data.map(
+ (item: any) => item.metadata?.sourceURL
+ );
+ expect(urls.length).toBeGreaterThanOrEqual(1);
+
+ // Check if all URLs have an absolute maximum depth of 3 after the base URL depth was 2 and the maxDepth was 1
+ urls.forEach((url: string) => {
+ const pathSplits = new URL(url).pathname.split('/');
+ const depth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
expect(depth).toBeLessThanOrEqual(1);
});
- }, 120000);
+ }, 180000);
- // it("should return a successful response with a valid API key and valid limit option", async () => {
+ it.concurrent("should return a successful response with relative max depth option for a valid crawl job with maxDepth equals to 2", async () => {
+ const crawlResponse = await request(TEST_URL)
+ .post("/v0/crawl")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({
+ url: "https://www.scrapethissite.com",
+ crawlerOptions: { maxDepth: 2, limit: 5 },
+ });
+ expect(crawlResponse.statusCode).toBe(200);
+
+ const response = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("status");
+ expect(["active", "waiting"]).toContain(response.body.status);
+ // wait for 60 seconds
+ let isCompleted = false;
+ while (!isCompleted) {
+ const statusCheckResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(statusCheckResponse.statusCode).toBe(200);
+ isCompleted = statusCheckResponse.body.status === "completed";
+ if (!isCompleted) {
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second before checking again
+ }
+ }
+ const completedResponse = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+
+ expect(completedResponse.statusCode).toBe(200);
+ expect(completedResponse.body).toHaveProperty("status");
+ expect(completedResponse.body.status).toBe("completed");
+ expect(completedResponse.body).toHaveProperty("data");
+ expect(completedResponse.body.data[0]).toHaveProperty("content");
+ expect(completedResponse.body.data[0]).toHaveProperty("markdown");
+ expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ const urls = completedResponse.body.data.map(
+ (item: any) => item.metadata?.sourceURL
+ );
+ expect(urls.length).toBeGreaterThanOrEqual(1);
+
+ // Check if all URLs have an absolute maximum depth of 3 after the base URL depth was 2 and the maxDepth was 1
+ urls.forEach((url: string) => {
+ const pathSplits = new URL(url).pathname.split('/');
+ const depth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
+ expect(depth).toBeLessThanOrEqual(3);
+ });
+ }, 180000);
+
+ // it.concurrent("should return a successful response with a valid API key and valid limit option", async () => {
// const crawlResponse = await request(TEST_URL)
// .post("/v0/crawl")
// .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -395,13 +769,13 @@ describe("E2E Tests for API Routes", () => {
// expect(completedResponse.body.data[0].content).not.toContain("main menu");
// }, 60000); // 60 seconds
- it("should return a successful response for a valid crawl job with includeHtml set to true option", async () => {
+ it.concurrent("should return a successful response for a valid crawl job with includeHtml set to true option (1)", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({
- url: "https://firecrawl.dev",
+ url: "https://roastmywebsite.ai",
pageOptions: { includeHtml: true },
});
expect(crawlResponse.statusCode).toBe(200);
@@ -411,7 +785,7 @@ describe("E2E Tests for API Routes", () => {
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("status");
- expect(response.body.status).toBe("active");
+ expect(["active", "waiting"]).toContain(response.body.status);
let isCompleted = false;
while (!isCompleted) {
@@ -436,23 +810,29 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("content");
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
// 120 seconds
expect(completedResponse.body.data[0]).toHaveProperty("html");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
- expect(completedResponse.body.data[0].content).toContain("🔥 Firecrawl");
- expect(completedResponse.body.data[0].markdown).toContain("Firecrawl");
+ expect(completedResponse.body.data[0].content).toContain("_Roast_");
+ expect(completedResponse.body.data[0].markdown).toContain("_Roast_");
expect(completedResponse.body.data[0].html).toContain(" {
- it("should require authorization", async () => {
+ it.concurrent("should require authorization", async () => {
const response = await request(TEST_URL).post("/v0/crawlWebsitePreview");
expect(response.statusCode).toBe(401);
});
- it("should return an error response with an invalid API key", async () => {
+ it.concurrent("should return an error response with an invalid API key", async () => {
const response = await request(TEST_URL)
.post("/v0/crawlWebsitePreview")
.set("Authorization", `Bearer invalid-api-key`)
@@ -461,7 +841,7 @@ describe("E2E Tests for API Routes", () => {
expect(response.statusCode).toBe(401);
});
- // it("should return an error for a blocklisted URL", async () => {
+ // it.concurrent("should return an error for a blocklisted URL", async () => {
// const blocklistedUrl = "https://instagram.com/fake-test";
// const response = await request(TEST_URL)
// .post("/v0/crawlWebsitePreview")
@@ -473,7 +853,7 @@ describe("E2E Tests for API Routes", () => {
// expect(response.body.error).toContain("Firecrawl currently does not support social media scraping due to policy restrictions. We're actively working on building support for it.");
// });
- it("should return a timeout error when scraping takes longer than the specified timeout", async () => {
+ it.concurrent("should return a timeout error when scraping takes longer than the specified timeout", async () => {
const response = await request(TEST_URL)
.post("/v0/scrape")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -483,27 +863,27 @@ describe("E2E Tests for API Routes", () => {
expect(response.statusCode).toBe(408);
}, 3000);
- it("should return a successful response with a valid API key", async () => {
- const response = await request(TEST_URL)
- .post("/v0/crawlWebsitePreview")
- .set("Authorization", `Bearer this_is_just_a_preview_token`)
- .set("Content-Type", "application/json")
- .send({ url: "https://firecrawl.dev" });
- expect(response.statusCode).toBe(200);
- expect(response.body).toHaveProperty("jobId");
- expect(response.body.jobId).toMatch(
- /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
- );
- });
+ // it.concurrent("should return a successful response with a valid API key for crawlWebsitePreview", async () => {
+ // const response = await request(TEST_URL)
+ // .post("/v0/crawlWebsitePreview")
+ // .set("Authorization", `Bearer this_is_just_a_preview_token`)
+ // .set("Content-Type", "application/json")
+ // .send({ url: "https://firecrawl.dev" });
+ // expect(response.statusCode).toBe(200);
+ // expect(response.body).toHaveProperty("jobId");
+ // expect(response.body.jobId).toMatch(
+ // /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/
+ // );
+ // });
});
describe("POST /v0/search", () => {
- it("should require authorization", async () => {
+ it.concurrent("should require authorization", async () => {
const response = await request(TEST_URL).post("/v0/search");
expect(response.statusCode).toBe(401);
});
- it("should return an error response with an invalid API key", async () => {
+ it.concurrent("should return an error response with an invalid API key", async () => {
const response = await request(TEST_URL)
.post("/v0/search")
.set("Authorization", `Bearer invalid-api-key`)
@@ -512,7 +892,7 @@ describe("E2E Tests for API Routes", () => {
expect(response.statusCode).toBe(401);
});
- it("should return a successful response with a valid API key", async () => {
+ it.concurrent("should return a successful response with a valid API key for search", async () => {
const response = await request(TEST_URL)
.post("/v0/search")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -526,31 +906,31 @@ describe("E2E Tests for API Routes", () => {
});
describe("GET /v0/crawl/status/:jobId", () => {
- it("should require authorization", async () => {
+ it.concurrent("should require authorization", async () => {
const response = await request(TEST_URL).get("/v0/crawl/status/123");
expect(response.statusCode).toBe(401);
});
- it("should return an error response with an invalid API key", async () => {
+ it.concurrent("should return an error response with an invalid API key", async () => {
const response = await request(TEST_URL)
.get("/v0/crawl/status/123")
.set("Authorization", `Bearer invalid-api-key`);
expect(response.statusCode).toBe(401);
});
- it("should return Job not found for invalid job ID", async () => {
+ it.concurrent("should return Job not found for invalid job ID", async () => {
const response = await request(TEST_URL)
.get("/v0/crawl/status/invalidJobId")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
expect(response.statusCode).toBe(404);
});
- it("should return a successful crawl status response for a valid crawl job", async () => {
+ it.concurrent("should return a successful crawl status response for a valid crawl job", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
- .send({ url: "https://firecrawl.dev" });
+ .send({ url: "https://mendable.ai/blog" });
expect(crawlResponse.statusCode).toBe(200);
let isCompleted = false;
@@ -576,10 +956,18 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("content");
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
- expect(completedResponse.body.data[0].content).toContain("🔥 Firecrawl");
- }, 60000); // 60 seconds
+ expect(completedResponse.body.data[0].content).toContain("Mendable");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
+
+ const childrenLinks = completedResponse.body.data.filter(doc =>
+ doc.metadata && doc.metadata.sourceURL && doc.metadata.sourceURL.includes("mendable.ai/blog")
+ );
+
+ expect(childrenLinks.length).toBe(completedResponse.body.data.length);
+ }, 180000); // 120 seconds
- it('should return a successful response for a valid crawl job with PDF files without explicit .pdf extension', async () => {
+ it.concurrent('should return a successful response for a valid crawl job with PDF files without explicit .pdf extension ', async () => {
const crawlResponse = await request(TEST_URL)
.post('/v0/crawl')
.set('Authorization', `Bearer ${process.env.TEST_API_KEY}`)
@@ -614,16 +1002,21 @@ describe("E2E Tests for API Routes", () => {
})
])
);
- }, 60000); // 60 seconds
- it("should return a successful response with max depth option for a valid crawl job", async () => {
+ expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
+ }, 180000); // 120 seconds
+
+
+ it.concurrent("should return a successful response with max depth option for a valid crawl job", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({
url: "https://www.scrapethissite.com",
- crawlerOptions: { maxDepth: 2 },
+ crawlerOptions: { maxDepth: 1 },
});
expect(crawlResponse.statusCode).toBe(200);
@@ -649,6 +1042,9 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("content");
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
+
const urls = completedResponse.body.data.map(
(item: any) => item.metadata?.sourceURL
);
@@ -656,18 +1052,19 @@ describe("E2E Tests for API Routes", () => {
// Check if all URLs have a maximum depth of 1
urls.forEach((url) => {
- const depth = new URL(url).pathname.split("/").filter(Boolean).length;
- expect(depth).toBeLessThanOrEqual(1);
+ const pathSplits = new URL(url).pathname.split('/');
+ const depth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
+ expect(depth).toBeLessThanOrEqual(2);
});
- }, 120000);
+ }, 180000);
- it("should return a successful response for a valid crawl job with includeHtml set to true option", async () => {
+ it.concurrent("should return a successful response for a valid crawl job with includeHtml set to true option (2)", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({
- url: "https://firecrawl.dev",
+ url: "https://roastmywebsite.ai",
pageOptions: { includeHtml: true },
});
expect(crawlResponse.statusCode).toBe(200);
@@ -677,14 +1074,25 @@ describe("E2E Tests for API Routes", () => {
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("status");
- expect(response.body.status).toBe("active");
+ expect(["active", "waiting"]).toContain(response.body.status);
- // wait for 30 seconds
- await new Promise((r) => setTimeout(r, 30000));
+ let isFinished = false;
+ let completedResponse;
- const completedResponse = await request(TEST_URL)
- .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
- .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ while (!isFinished) {
+ const response = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("status");
+
+ if (response.body.status === "completed") {
+ isFinished = true;
+ completedResponse = response;
+ } else {
+ await new Promise((r) => setTimeout(r, 1000)); // Wait for 1 second before checking again
+ }
+ }
expect(completedResponse.statusCode).toBe(200);
expect(completedResponse.body).toHaveProperty("status");
@@ -693,53 +1101,103 @@ describe("E2E Tests for API Routes", () => {
expect(completedResponse.body.data[0]).toHaveProperty("content");
expect(completedResponse.body.data[0]).toHaveProperty("markdown");
expect(completedResponse.body.data[0]).toHaveProperty("metadata");
-
- // 120 seconds
expect(completedResponse.body.data[0]).toHaveProperty("html");
- expect(completedResponse.body.data[0]).toHaveProperty("metadata");
- expect(completedResponse.body.data[0].content).toContain("🔥 Firecrawl");
- expect(completedResponse.body.data[0].markdown).toContain("Firecrawl");
+ expect(completedResponse.body.data[0].content).toContain("_Roast_");
+ expect(completedResponse.body.data[0].markdown).toContain("_Roast_");
expect(completedResponse.body.data[0].html).toContain(" {
+ it.concurrent("should return a successful response for a valid crawl job with allowBackwardCrawling set to true option", async () => {
+ const crawlResponse = await request(TEST_URL)
+ .post("/v0/crawl")
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
+ .set("Content-Type", "application/json")
+ .send({
+ url: "https://mendable.ai/blog",
+ pageOptions: { includeHtml: true },
+ crawlerOptions: { allowBackwardCrawling: true },
+ });
+ expect(crawlResponse.statusCode).toBe(200);
+
+ let isFinished = false;
+ let completedResponse;
+
+ while (!isFinished) {
+ const response = await request(TEST_URL)
+ .get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
+ .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+ expect(response.statusCode).toBe(200);
+ expect(response.body).toHaveProperty("status");
+
+ if (response.body.status === "completed") {
+ isFinished = true;
+ completedResponse = response;
+ } else {
+ await new Promise((r) => setTimeout(r, 1000)); // Wait for 1 second before checking again
+ }
+ }
+
+ expect(completedResponse.statusCode).toBe(200);
+ expect(completedResponse.body).toHaveProperty("status");
+ expect(completedResponse.body.status).toBe("completed");
+ expect(completedResponse.body).toHaveProperty("data");
+ expect(completedResponse.body.data[0]).toHaveProperty("content");
+ expect(completedResponse.body.data[0]).toHaveProperty("markdown");
+ expect(completedResponse.body.data[0]).toHaveProperty("metadata");
+ expect(completedResponse.body.data[0]).toHaveProperty("html");
+ expect(completedResponse.body.data[0].content).toContain("Mendable");
+ expect(completedResponse.body.data[0].markdown).toContain("Mendable");
+ expect(completedResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.data[0].metadata.pageError).toBeUndefined();
+
+ const onlyChildrenLinks = completedResponse.body.data.filter(doc => {
+ return doc.metadata && doc.metadata.sourceURL && doc.metadata.sourceURL.includes("mendable.ai/blog")
+ });
+
+ expect(completedResponse.body.data.length).toBeGreaterThan(onlyChildrenLinks.length);
+ }, 60000);
+
+ it.concurrent("If someone cancels a crawl job, it should turn into failed status", async () => {
const crawlResponse = await request(TEST_URL)
.post("/v0/crawl")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
.set("Content-Type", "application/json")
.send({ url: "https://jestjs.io" });
+
expect(crawlResponse.statusCode).toBe(200);
- // wait for 30 seconds
await new Promise((r) => setTimeout(r, 20000));
- const response = await request(TEST_URL)
+ const responseCancel = await request(TEST_URL)
.delete(`/v0/crawl/cancel/${crawlResponse.body.jobId}`)
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
- expect(response.statusCode).toBe(200);
- expect(response.body).toHaveProperty("status");
- expect(response.body.status).toBe("cancelled");
+ expect(responseCancel.statusCode).toBe(200);
+ expect(responseCancel.body).toHaveProperty("status");
+ expect(responseCancel.body.status).toBe("cancelled");
await new Promise((r) => setTimeout(r, 10000));
-
const completedResponse = await request(TEST_URL)
.get(`/v0/crawl/status/${crawlResponse.body.jobId}`)
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`);
+
expect(completedResponse.statusCode).toBe(200);
expect(completedResponse.body).toHaveProperty("status");
expect(completedResponse.body.status).toBe("failed");
expect(completedResponse.body).toHaveProperty("data");
- expect(completedResponse.body.data).toEqual(null);
+ expect(completedResponse.body.data).toBeNull();
expect(completedResponse.body).toHaveProperty("partial_data");
expect(completedResponse.body.partial_data[0]).toHaveProperty("content");
expect(completedResponse.body.partial_data[0]).toHaveProperty("markdown");
expect(completedResponse.body.partial_data[0]).toHaveProperty("metadata");
-
+ expect(completedResponse.body.partial_data[0].metadata.pageStatusCode).toBe(200);
+ expect(completedResponse.body.partial_data[0].metadata.pageError).toBeUndefined();
}, 60000); // 60 seconds
describe("POST /v0/scrape with LLM Extraction", () => {
- it("should extract data using LLM extraction mode", async () => {
+ it.concurrent("should extract data using LLM extraction mode", async () => {
const response = await request(TEST_URL)
.post("/v0/scrape")
.set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -790,7 +1248,7 @@ describe("E2E Tests for API Routes", () => {
});
// describe("POST /v0/scrape for Top 100 Companies", () => {
- // it("should extract data for the top 100 companies", async () => {
+ // it.concurrent("should extract data for the top 100 companies", async () => {
// const response = await request(TEST_URL)
// .post("/v0/scrape")
// .set("Authorization", `Bearer ${process.env.TEST_API_KEY}`)
@@ -848,7 +1306,7 @@ describe("E2E Tests for API Routes", () => {
// });
describe("POST /v0/crawl with fast mode", () => {
- it("should complete the crawl under 20 seconds", async () => {
+ it.concurrent("should complete the crawl under 20 seconds", async () => {
const startTime = Date.now();
const crawlResponse = await request(TEST_URL)
@@ -881,15 +1339,19 @@ describe("E2E Tests for API Routes", () => {
}
}
- const endTime = Date.now();
- const timeElapsed = (endTime - startTime) / 1000; // Convert to seconds
+ // const endTime = Date.now();
+ // const timeElapsed = (endTime - startTime) / 1000; // Convert to seconds
- console.log(`Time elapsed: ${timeElapsed} seconds`);
+ // console.log(`Time elapsed: ${timeElapsed} seconds`);
expect(statusResponse.body.status).toBe("completed");
expect(statusResponse.body).toHaveProperty("data");
expect(statusResponse.body.data[0]).toHaveProperty("content");
expect(statusResponse.body.data[0]).toHaveProperty("markdown");
+ expect(statusResponse.body.data[0]).toHaveProperty("metadata");
+ expect(statusResponse.body.data[0].metadata.pageStatusCode).toBe(200);
+ expect(statusResponse.body.data[0].metadata.pageError).toBeUndefined();
+
const results = statusResponse.body.data;
// results.forEach((result, i) => {
// console.log(result.metadata.sourceURL);
@@ -899,7 +1361,7 @@ describe("E2E Tests for API Routes", () => {
}, 20000);
- // it("should complete the crawl in more than 10 seconds", async () => {
+ // it.concurrent("should complete the crawl in more than 10 seconds", async () => {
// const startTime = Date.now();
// const crawlResponse = await request(TEST_URL)
@@ -949,7 +1411,7 @@ describe("E2E Tests for API Routes", () => {
});
describe("GET /is-production", () => {
- it("should return the production status", async () => {
+ it.concurrent("should return the production status", async () => {
const response = await request(TEST_URL).get("/is-production");
expect(response.statusCode).toBe(200);
expect(response.body).toHaveProperty("isProduction");
@@ -957,7 +1419,7 @@ describe("E2E Tests for API Routes", () => {
});
describe("Rate Limiter", () => {
- it("should return 429 when rate limit is exceeded for preview token", async () => {
+ it.concurrent("should return 429 when rate limit is exceeded for preview token", async () => {
for (let i = 0; i < 5; i++) {
const response = await request(TEST_URL)
.post("/v0/scrape")
@@ -974,10 +1436,10 @@ describe("E2E Tests for API Routes", () => {
.send({ url: "https://www.scrapethissite.com" });
expect(response.statusCode).toBe(429);
- }, 60000);
+ }, 90000);
});
- // it("should return 429 when rate limit is exceeded for API key", async () => {
+ // it.concurrent("should return 429 when rate limit is exceeded for API key", async () => {
// for (let i = 0; i < parseInt(process.env.RATE_LIMIT_TEST_API_KEY_SCRAPE); i++) {
// const response = await request(TEST_URL)
// .post("/v0/scrape")
@@ -997,7 +1459,7 @@ describe("E2E Tests for API Routes", () => {
// expect(response.statusCode).toBe(429);
// }, 60000);
- // it("should return 429 when rate limit is exceeded for API key", async () => {
+ // it.concurrent("should return 429 when rate limit is exceeded for API key", async () => {
// for (let i = 0; i < parseInt(process.env.RATE_LIMIT_TEST_API_KEY_CRAWL); i++) {
// const response = await request(TEST_URL)
// .post("/v0/crawl")
diff --git a/apps/api/src/controllers/auth.ts b/apps/api/src/controllers/auth.ts
index b0bfabb..ea789fe 100644
--- a/apps/api/src/controllers/auth.ts
+++ b/apps/api/src/controllers/auth.ts
@@ -1,12 +1,13 @@
import { parseApi } from "../../src/lib/parseApi";
-import { getRateLimiter, } from "../../src/services/rate-limiter";
-import { AuthResponse, RateLimiterMode } from "../../src/types";
+import { getRateLimiter, } from "../../src/services/rate-limiter";
+import { AuthResponse, NotificationType, RateLimiterMode } from "../../src/types";
import { supabase_service } from "../../src/services/supabase";
import { withAuth } from "../../src/lib/withAuth";
import { RateLimiterRedis } from "rate-limiter-flexible";
import { setTraceAttributes } from '@hyperdx/node-opentelemetry';
+import { sendNotification } from "../services/notification/email_notification";
-export async function authenticateUser(req, res, mode?: RateLimiterMode) : Promise {
+export async function authenticateUser(req, res, mode?: RateLimiterMode): Promise {
return withAuth(supaAuthenticateUser)(req, res, mode);
}
function setTrace(team_id: string, api_key: string) {
@@ -18,7 +19,7 @@ function setTrace(team_id: string, api_key: string) {
} catch (error) {
console.error('Error setting trace attributes:', error);
}
-
+
}
export async function supaAuthenticateUser(
req,
@@ -29,6 +30,7 @@ export async function supaAuthenticateUser(
team_id?: string;
error?: string;
status?: number;
+ plan?: string;
}> {
const authHeader = req.headers.authorization;
if (!authHeader) {
@@ -51,8 +53,11 @@ export async function supaAuthenticateUser(
let subscriptionData: { team_id: string, plan: string } | null = null;
let normalizedApi: string;
+ let team_id: string;
+
if (token == "this_is_just_a_preview_token") {
rateLimiter = getRateLimiter(RateLimiterMode.Preview, token);
+ team_id = "preview";
} else {
normalizedApi = parseApi(token);
@@ -89,7 +94,9 @@ export async function supaAuthenticateUser(
status: 401,
};
}
- const team_id = data[0].team_id;
+ const internal_team_id = data[0].team_id;
+ team_id = internal_team_id;
+
const plan = getPlanByPriceId(data[0].price_id);
// HyperDX Logging
setTrace(team_id, normalizedApi);
@@ -97,19 +104,20 @@ export async function supaAuthenticateUser(
team_id: team_id,
plan: plan
}
- switch (mode) {
+ switch (mode) {
case RateLimiterMode.Crawl:
rateLimiter = getRateLimiter(RateLimiterMode.Crawl, token, subscriptionData.plan);
break;
case RateLimiterMode.Scrape:
rateLimiter = getRateLimiter(RateLimiterMode.Scrape, token, subscriptionData.plan);
break;
+ case RateLimiterMode.Search:
+ rateLimiter = getRateLimiter(RateLimiterMode.Search, token, subscriptionData.plan);
+ break;
case RateLimiterMode.CrawlStatus:
rateLimiter = getRateLimiter(RateLimiterMode.CrawlStatus, token);
break;
- case RateLimiterMode.Search:
- rateLimiter = getRateLimiter(RateLimiterMode.Search, token);
- break;
+
case RateLimiterMode.Preview:
rateLimiter = getRateLimiter(RateLimiterMode.Preview, token);
break;
@@ -122,13 +130,23 @@ export async function supaAuthenticateUser(
}
}
+ const team_endpoint_token = token === "this_is_just_a_preview_token" ? iptoken : team_id;
+
try {
- await rateLimiter.consume(iptoken);
+ await rateLimiter.consume(team_endpoint_token);
} catch (rateLimiterRes) {
console.error(rateLimiterRes);
+ const secs = Math.round(rateLimiterRes.msBeforeNext / 1000) || 1;
+ const retryDate = new Date(Date.now() + rateLimiterRes.msBeforeNext);
+
+ // We can only send a rate limit email every 7 days, send notification already has the date in between checking
+ const startDate = new Date();
+ const endDate = new Date();
+ endDate.setDate(endDate.getDate() + 7);
+ // await sendNotification(team_id, NotificationType.RATE_LIMIT_REACHED, startDate.toISOString(), endDate.toISOString());
return {
success: false,
- error: "Rate limit exceeded. Too many requests, try again in 1 minute.",
+ error: `Rate limit exceeded. Consumed points: ${rateLimiterRes.consumedPoints}, Remaining points: ${rateLimiterRes.remainingPoints}. Upgrade your plan at https://firecrawl.dev/pricing for increased rate limits or please retry after ${secs}s, resets at ${retryDate}`,
status: 429,
};
}
@@ -155,9 +173,9 @@ export async function supaAuthenticateUser(
normalizedApi = parseApi(token);
const { data, error } = await supabase_service
- .from("api_keys")
- .select("*")
- .eq("key", normalizedApi);
+ .from("api_keys")
+ .select("*")
+ .eq("key", normalizedApi);
if (error || !data || data.length === 0) {
return {
@@ -170,16 +188,24 @@ export async function supaAuthenticateUser(
subscriptionData = data[0];
}
- return { success: true, team_id: subscriptionData.team_id };
+ return { success: true, team_id: subscriptionData.team_id, plan: subscriptionData.plan ?? ""};
}
function getPlanByPriceId(price_id: string) {
switch (price_id) {
+ case process.env.STRIPE_PRICE_ID_STARTER:
+ return 'starter';
case process.env.STRIPE_PRICE_ID_STANDARD:
return 'standard';
case process.env.STRIPE_PRICE_ID_SCALE:
return 'scale';
+ case process.env.STRIPE_PRICE_ID_HOBBY || process.env.STRIPE_PRICE_ID_HOBBY_YEARLY:
+ return 'hobby';
+ case process.env.STRIPE_PRICE_ID_STANDARD_NEW || process.env.STRIPE_PRICE_ID_STANDARD_NEW_YEARLY:
+ return 'standard-new';
+ case process.env.STRIPE_PRICE_ID_GROWTH || process.env.STRIPE_PRICE_ID_GROWTH_YEARLY:
+ return 'growth';
default:
- return 'starter';
+ return 'free';
}
}
\ No newline at end of file
diff --git a/apps/api/src/controllers/crawl.ts b/apps/api/src/controllers/crawl.ts
index e53faed..8fd876d 100644
--- a/apps/api/src/controllers/crawl.ts
+++ b/apps/api/src/controllers/crawl.ts
@@ -7,6 +7,8 @@ import { RateLimiterMode } from "../../src/types";
import { addWebScraperJob } from "../../src/services/queue-jobs";
import { isUrlBlocked } from "../../src/scraper/WebScraper/utils/blocklist";
import { logCrawl } from "../../src/services/logging/crawl_log";
+import { validateIdempotencyKey } from "../../src/services/idempotency/validate";
+import { createIdempotencyKey } from "../../src/services/idempotency/create";
export async function crawlController(req: Request, res: Response) {
try {
@@ -19,6 +21,19 @@ export async function crawlController(req: Request, res: Response) {
return res.status(status).json({ error });
}
+ if (req.headers["x-idempotency-key"]) {
+ const isIdempotencyValid = await validateIdempotencyKey(req);
+ if (!isIdempotencyValid) {
+ return res.status(409).json({ error: "Idempotency key already used" });
+ }
+ try {
+ createIdempotencyKey(req);
+ } catch (error) {
+ console.error(error);
+ return res.status(500).json({ error: error.message });
+ }
+ }
+
const { success: creditsCheckSuccess, message: creditsCheckMessage } =
await checkTeamCredits(team_id, 1);
if (!creditsCheckSuccess) {
@@ -40,8 +55,16 @@ export async function crawlController(req: Request, res: Response) {
}
const mode = req.body.mode ?? "crawl";
- const crawlerOptions = req.body.crawlerOptions ?? {};
- const pageOptions = req.body.pageOptions ?? { onlyMainContent: false, includeHtml: false };
+
+ const crawlerOptions = req.body.crawlerOptions ?? {
+ allowBackwardCrawling: false
+ };
+ const pageOptions = req.body.pageOptions ?? {
+ onlyMainContent: false,
+ includeHtml: false,
+ removeTags: [],
+ parsePDF: true
+ };
if (mode === "single_urls" && !url.includes(",")) {
try {
@@ -49,9 +72,7 @@ export async function crawlController(req: Request, res: Response) {
await a.setOptions({
mode: "single_urls",
urls: [url],
- crawlerOptions: {
- returnOnlyUrls: true,
- },
+ crawlerOptions: { ...crawlerOptions, returnOnlyUrls: true },
pageOptions: pageOptions,
});
@@ -76,7 +97,7 @@ export async function crawlController(req: Request, res: Response) {
const job = await addWebScraperJob({
url: url,
mode: mode ?? "crawl", // fix for single urls not working
- crawlerOptions: { ...crawlerOptions },
+ crawlerOptions: crawlerOptions,
team_id: team_id,
pageOptions: pageOptions,
origin: req.body.origin ?? "api",
diff --git a/apps/api/src/controllers/crawlPreview.ts b/apps/api/src/controllers/crawlPreview.ts
index d3e9afe..2c3dc4e 100644
--- a/apps/api/src/controllers/crawlPreview.ts
+++ b/apps/api/src/controllers/crawlPreview.ts
@@ -26,7 +26,7 @@ export async function crawlPreviewController(req: Request, res: Response) {
const mode = req.body.mode ?? "crawl";
const crawlerOptions = req.body.crawlerOptions ?? {};
- const pageOptions = req.body.pageOptions ?? { onlyMainContent: false, includeHtml: false };
+ const pageOptions = req.body.pageOptions ?? { onlyMainContent: false, includeHtml: false, removeTags: [] };
const job = await addWebScraperJob({
url: url,
diff --git a/apps/api/src/controllers/scrape.ts b/apps/api/src/controllers/scrape.ts
index 0b3f146..1537c07 100644
--- a/apps/api/src/controllers/scrape.ts
+++ b/apps/api/src/controllers/scrape.ts
@@ -15,7 +15,8 @@ export async function scrapeHelper(
crawlerOptions: any,
pageOptions: PageOptions,
extractorOptions: ExtractorOptions,
- timeout: number
+ timeout: number,
+ plan?: string
): Promise<{
success: boolean;
error?: string;
@@ -60,11 +61,13 @@ export async function scrapeHelper(
(doc: { content?: string }) => doc.content && doc.content.trim().length > 0
);
if (filteredDocs.length === 0) {
- return { success: true, error: "No page found", returnCode: 200 };
+ return { success: true, error: "No page found", returnCode: 200, data: docs[0] };
}
let creditsToBeBilled = filteredDocs.length;
- const creditsPerLLMExtract = 5;
+ const creditsPerLLMExtract = 50;
+
+
if (extractorOptions.mode === "llm-extraction") {
creditsToBeBilled = creditsToBeBilled + (creditsPerLLMExtract * filteredDocs.length);
@@ -93,7 +96,7 @@ export async function scrapeHelper(
export async function scrapeController(req: Request, res: Response) {
try {
// make sure to authenticate user first, Bearer
- const { success, team_id, error, status } = await authenticateUser(
+ const { success, team_id, error, status, plan } = await authenticateUser(
req,
res,
RateLimiterMode.Scrape
@@ -102,7 +105,13 @@ export async function scrapeController(req: Request, res: Response) {
return res.status(status).json({ error });
}
const crawlerOptions = req.body.crawlerOptions ?? {};
- const pageOptions = req.body.pageOptions ?? { onlyMainContent: false, includeHtml: false };
+ const pageOptions = req.body.pageOptions ?? {
+ onlyMainContent: false,
+ includeHtml: false,
+ waitFor: 0,
+ screenshot: false,
+ parsePDF: true
+ };
const extractorOptions = req.body.extractorOptions ?? {
mode: "markdown"
}
@@ -129,7 +138,8 @@ export async function scrapeController(req: Request, res: Response) {
crawlerOptions,
pageOptions,
extractorOptions,
- timeout
+ timeout,
+ plan
);
const endTime = new Date().getTime();
const timeTakenInSeconds = (endTime - startTime) / 1000;
diff --git a/apps/api/src/controllers/search.ts b/apps/api/src/controllers/search.ts
index d98c08d..b555197 100644
--- a/apps/api/src/controllers/search.ts
+++ b/apps/api/src/controllers/search.ts
@@ -28,11 +28,13 @@ export async function searchHelper(
const tbs = searchOptions.tbs ?? null;
const filter = searchOptions.filter ?? null;
+ const num_results = searchOptions.limit ?? 7;
+ const num_results_buffer = Math.floor(num_results * 1.5);
let res = await search({
query: query,
advanced: advanced,
- num_results: searchOptions.limit ?? 7,
+ num_results: num_results_buffer,
tbs: tbs,
filter: filter,
lang: searchOptions.lang ?? "en",
@@ -41,12 +43,28 @@ export async function searchHelper(
});
let justSearch = pageOptions.fetchPageContent === false;
+
if (justSearch) {
+ const billingResult = await billTeam(
+ team_id,
+ res.length
+ );
+ if (!billingResult.success) {
+ return {
+ success: false,
+ error:
+ "Failed to bill team. Insufficient credits or subscription not found.",
+ returnCode: 402,
+ };
+ }
return { success: true, data: res, returnCode: 200 };
}
res = res.filter((r) => !isUrlBlocked(r.url));
+ if (res.length > num_results) {
+ res = res.slice(0, num_results);
+ }
if (res.length === 0) {
return { success: true, error: "No search results found", returnCode: 200 };
@@ -67,6 +85,7 @@ export async function searchHelper(
onlyMainContent: pageOptions?.onlyMainContent ?? true,
fetchPageContent: pageOptions?.fetchPageContent ?? true,
includeHtml: pageOptions?.includeHtml ?? false,
+ removeTags: pageOptions?.removeTags ?? [],
fallback: false,
},
});
@@ -82,7 +101,7 @@ export async function searchHelper(
);
if (filteredDocs.length === 0) {
- return { success: true, error: "No page found", returnCode: 200 };
+ return { success: true, error: "No page found", returnCode: 200, data: docs };
}
const billingResult = await billTeam(
@@ -121,6 +140,7 @@ export async function searchController(req: Request, res: Response) {
includeHtml: false,
onlyMainContent: true,
fetchPageContent: true,
+ removeTags: [],
fallback: false,
};
const origin = req.body.origin ?? "api";
diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts
index 326728e..494b4d5 100644
--- a/apps/api/src/index.ts
+++ b/apps/api/src/index.ts
@@ -5,166 +5,215 @@ import "dotenv/config";
import { getWebScraperQueue } from "./services/queue-service";
import { redisClient } from "./services/rate-limiter";
import { v0Router } from "./routes/v0";
-import { initSDK } from '@hyperdx/node-opentelemetry';
+import { initSDK } from "@hyperdx/node-opentelemetry";
+import cluster from "cluster";
+import os from "os";
const { createBullBoard } = require("@bull-board/api");
const { BullAdapter } = require("@bull-board/api/bullAdapter");
const { ExpressAdapter } = require("@bull-board/express");
-export const app = express();
+const numCPUs = process.env.ENV === "local" ? 2 : os.cpus().length;
+console.log(`Number of CPUs: ${numCPUs} available`);
-global.isProduction = process.env.IS_PRODUCTION === "true";
+if (cluster.isMaster) {
+ console.log(`Master ${process.pid} is running`);
-app.use(bodyParser.urlencoded({ extended: true }));
-app.use(bodyParser.json({ limit: "10mb" }));
+ // Fork workers.
+ for (let i = 0; i < numCPUs; i++) {
+ cluster.fork();
+ }
-app.use(cors()); // Add this line to enable CORS
-
-const serverAdapter = new ExpressAdapter();
-serverAdapter.setBasePath(`/admin/${process.env.BULL_AUTH_KEY}/queues`);
-
-const { addQueue, removeQueue, setQueues, replaceQueues } = createBullBoard({
- queues: [new BullAdapter(getWebScraperQueue())],
- serverAdapter: serverAdapter,
-});
-
-app.use(
- `/admin/${process.env.BULL_AUTH_KEY}/queues`,
- serverAdapter.getRouter()
-);
-
-app.get("/", (req, res) => {
- res.send("SCRAPERS-JS: Hello, world! Fly.io");
-});
-
-//write a simple test function
-app.get("/test", async (req, res) => {
- res.send("Hello, world!");
-});
-
-// register router
-app.use(v0Router);
-
-const DEFAULT_PORT = process.env.PORT ?? 3002;
-const HOST = process.env.HOST ?? "localhost";
-redisClient.connect();
-
-// HyperDX OpenTelemetry
-if(process.env.ENV === 'production') {
- initSDK({ consoleCapture: true, additionalInstrumentations: []});
-}
-
-
-export function startServer(port = DEFAULT_PORT) {
- const server = app.listen(Number(port), HOST, () => {
- console.log(`Server listening on port ${port}`);
- console.log(
- `For the UI, open http://${HOST}:${port}/admin/${process.env.BULL_AUTH_KEY}/queues`
- );
- console.log("");
- console.log("1. Make sure Redis is running on port 6379 by default");
- console.log(
- "2. If you want to run nango, make sure you do port forwarding in 3002 using ngrok http 3002 "
- );
+ cluster.on("exit", (worker, code, signal) => {
+ console.log(`Worker ${worker.process.pid} exited`);
+ console.log("Starting a new worker");
+ cluster.fork();
});
- return server;
-}
+} else {
+ const app = express();
-if (require.main === module) {
- startServer();
-}
+ global.isProduction = process.env.IS_PRODUCTION === "true";
-// Use this as a "health check" that way we dont destroy the server
-app.get(`/admin/${process.env.BULL_AUTH_KEY}/queues`, async (req, res) => {
- try {
- const webScraperQueue = getWebScraperQueue();
- const [webScraperActive] = await Promise.all([
- webScraperQueue.getActiveCount(),
- ]);
+ app.use(bodyParser.urlencoded({ extended: true }));
+ app.use(bodyParser.json({ limit: "10mb" }));
- const noActiveJobs = webScraperActive === 0;
- // 200 if no active jobs, 503 if there are active jobs
- return res.status(noActiveJobs ? 200 : 500).json({
- webScraperActive,
- noActiveJobs,
- });
- } catch (error) {
- console.error(error);
- return res.status(500).json({ error: error.message });
+ app.use(cors()); // Add this line to enable CORS
+
+ const serverAdapter = new ExpressAdapter();
+ serverAdapter.setBasePath(`/admin/${process.env.BULL_AUTH_KEY}/queues`);
+
+ const { addQueue, removeQueue, setQueues, replaceQueues } = createBullBoard({
+ queues: [new BullAdapter(getWebScraperQueue())],
+ serverAdapter: serverAdapter,
+ });
+
+ app.use(
+ `/admin/${process.env.BULL_AUTH_KEY}/queues`,
+ serverAdapter.getRouter()
+ );
+
+ app.get("/", (req, res) => {
+ res.send("SCRAPERS-JS: Hello, world! Fly.io");
+ });
+
+ //write a simple test function
+ app.get("/test", async (req, res) => {
+ res.send("Hello, world!");
+ });
+
+ // register router
+ app.use(v0Router);
+
+ const DEFAULT_PORT = process.env.PORT ?? 3002;
+ const HOST = process.env.HOST ?? "localhost";
+ redisClient.connect();
+
+ // HyperDX OpenTelemetry
+ if (process.env.ENV === "production") {
+ initSDK({ consoleCapture: true, additionalInstrumentations: [] });
}
-});
-app.get(`/serverHealthCheck`, async (req, res) => {
- try {
- const webScraperQueue = getWebScraperQueue();
- const [waitingJobs] = await Promise.all([
- webScraperQueue.getWaitingCount(),
- ]);
-
- const noWaitingJobs = waitingJobs === 0;
- // 200 if no active jobs, 503 if there are active jobs
- return res.status(noWaitingJobs ? 200 : 500).json({
- waitingJobs,
+ function startServer(port = DEFAULT_PORT) {
+ const server = app.listen(Number(port), HOST, () => {
+ console.log(`Worker ${process.pid} listening on port ${port}`);
+ console.log(
+ `For the UI, open http://${HOST}:${port}/admin/${process.env.BULL_AUTH_KEY}/queues`
+ );
+ console.log("");
+ console.log("1. Make sure Redis is running on port 6379 by default");
+ console.log(
+ "2. If you want to run nango, make sure you do port forwarding in 3002 using ngrok http 3002 "
+ );
});
- } catch (error) {
- console.error(error);
- return res.status(500).json({ error: error.message });
+ return server;
}
-});
-app.get('/serverHealthCheck/notify', async (req, res) => {
- if (process.env.SLACK_WEBHOOK_URL) {
- const treshold = 1; // The treshold value for the active jobs
- const timeout = 60000; // 1 minute // The timeout value for the check in milliseconds
+ if (require.main === module) {
+ startServer();
+ }
- const getWaitingJobsCount = async () => {
+ // Use this as a "health check" that way we dont destroy the server
+ app.get(`/admin/${process.env.BULL_AUTH_KEY}/queues`, async (req, res) => {
+ try {
const webScraperQueue = getWebScraperQueue();
- const [waitingJobsCount] = await Promise.all([
+ const [webScraperActive] = await Promise.all([
+ webScraperQueue.getActiveCount(),
+ ]);
+
+ const noActiveJobs = webScraperActive === 0;
+ // 200 if no active jobs, 503 if there are active jobs
+ return res.status(noActiveJobs ? 200 : 500).json({
+ webScraperActive,
+ noActiveJobs,
+ });
+ } catch (error) {
+ console.error(error);
+ return res.status(500).json({ error: error.message });
+ }
+ });
+
+ app.get(`/serverHealthCheck`, async (req, res) => {
+ try {
+ const webScraperQueue = getWebScraperQueue();
+ const [waitingJobs] = await Promise.all([
webScraperQueue.getWaitingCount(),
]);
- return waitingJobsCount;
- };
+ const noWaitingJobs = waitingJobs === 0;
+ // 200 if no active jobs, 503 if there are active jobs
+ return res.status(noWaitingJobs ? 200 : 500).json({
+ waitingJobs,
+ });
+ } catch (error) {
+ console.error(error);
+ return res.status(500).json({ error: error.message });
+ }
+ });
- res.status(200).json({ message: "Check initiated" });
+ app.get("/serverHealthCheck/notify", async (req, res) => {
+ if (process.env.SLACK_WEBHOOK_URL) {
+ const treshold = 1; // The treshold value for the active jobs
+ const timeout = 60000; // 1 minute // The timeout value for the check in milliseconds
- const checkWaitingJobs = async () => {
- try {
- let waitingJobsCount = await getWaitingJobsCount();
- if (waitingJobsCount >= treshold) {
- setTimeout(async () => {
- // Re-check the waiting jobs count after the timeout
- waitingJobsCount = await getWaitingJobsCount();
- if (waitingJobsCount >= treshold) {
- const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;
- const message = {
- text: `⚠️ Warning: The number of active jobs (${waitingJobsCount}) has exceeded the threshold (${treshold}) for more than ${timeout/60000} minute(s).`,
- };
+ const getWaitingJobsCount = async () => {
+ const webScraperQueue = getWebScraperQueue();
+ const [waitingJobsCount] = await Promise.all([
+ webScraperQueue.getWaitingCount(),
+ ]);
- const response = await fetch(slackWebhookUrl, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(message),
- })
-
- if (!response.ok) {
- console.error('Failed to send Slack notification')
+ return waitingJobsCount;
+ };
+
+ res.status(200).json({ message: "Check initiated" });
+
+ const checkWaitingJobs = async () => {
+ try {
+ let waitingJobsCount = await getWaitingJobsCount();
+ if (waitingJobsCount >= treshold) {
+ setTimeout(async () => {
+ // Re-check the waiting jobs count after the timeout
+ waitingJobsCount = await getWaitingJobsCount();
+ if (waitingJobsCount >= treshold) {
+ const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;
+ const message = {
+ text: `⚠️ Warning: The number of active jobs (${waitingJobsCount}) has exceeded the threshold (${treshold}) for more than ${
+ timeout / 60000
+ } minute(s).`,
+ };
+
+ const response = await fetch(slackWebhookUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(message),
+ });
+
+ if (!response.ok) {
+ console.error("Failed to send Slack notification");
+ }
}
- }
- }, timeout);
+ }, timeout);
+ }
+ } catch (error) {
+ console.error(error);
}
+ };
+
+ checkWaitingJobs();
+ }
+ });
+
+ app.get(
+ `/admin/${process.env.BULL_AUTH_KEY}/clean-before-24h-complete-jobs`,
+ async (req, res) => {
+ try {
+ const webScraperQueue = getWebScraperQueue();
+ const completedJobs = await webScraperQueue.getJobs(["completed"]);
+ const before24hJobs = completedJobs.filter(
+ (job) => job.finishedOn < Date.now() - 24 * 60 * 60 * 1000
+ );
+ const jobIds = before24hJobs.map((job) => job.id) as string[];
+ let count = 0;
+ for (const jobId of jobIds) {
+ try {
+ await webScraperQueue.removeJobs(jobId);
+ count++;
+ } catch (jobError) {
+ console.error(`Failed to remove job with ID ${jobId}:`, jobError);
+ }
+ }
+ res.status(200).send(`Removed ${count} completed jobs.`);
} catch (error) {
- console.error(error);
+ console.error("Failed to clean last 24h complete jobs:", error);
+ res.status(500).send("Failed to clean jobs");
}
- };
+ }
+ );
- checkWaitingJobs();
- }
-});
+ app.get("/is-production", (req, res) => {
+ res.send({ isProduction: global.isProduction });
+ });
-
-app.get("/is-production", (req, res) => {
- res.send({ isProduction: global.isProduction });
-});
+ console.log(`Worker ${process.pid} started`);
+}
diff --git a/apps/api/src/lib/LLM-extraction/index.ts b/apps/api/src/lib/LLM-extraction/index.ts
index ea6ddfd..6614dbd 100644
--- a/apps/api/src/lib/LLM-extraction/index.ts
+++ b/apps/api/src/lib/LLM-extraction/index.ts
@@ -1,4 +1,3 @@
-import Turndown from "turndown";
import OpenAI from "openai";
import Ajv from "ajv";
const ajv = new Ajv(); // Initialize AJV for JSON schema validation
diff --git a/apps/api/src/lib/entities.ts b/apps/api/src/lib/entities.ts
index ab0a0ef..12d8c36 100644
--- a/apps/api/src/lib/entities.ts
+++ b/apps/api/src/lib/entities.ts
@@ -15,6 +15,12 @@ export type PageOptions = {
includeHtml?: boolean;
fallback?: boolean;
fetchPageContent?: boolean;
+ waitFor?: number;
+ screenshot?: boolean;
+ headers?: Record;
+ replaceAllPathsWithAbsolutePaths?: boolean;
+ parsePDF?: boolean;
+ removeTags?: string | string[];
};
export type ExtractorOptions = {
@@ -32,20 +38,24 @@ export type SearchOptions = {
location?: string;
};
+export type CrawlerOptions = {
+ returnOnlyUrls?: boolean;
+ includes?: string[];
+ excludes?: string[];
+ maxCrawledLinks?: number;
+ maxDepth?: number;
+ limit?: number;
+ generateImgAltText?: boolean;
+ replaceAllPathsWithAbsolutePaths?: boolean;
+ ignoreSitemap?: boolean;
+ mode?: "default" | "fast"; // have a mode of some sort
+ allowBackwardCrawling?: boolean;
+}
+
export type WebScraperOptions = {
urls: string[];
mode: "single_urls" | "sitemap" | "crawl";
- crawlerOptions?: {
- returnOnlyUrls?: boolean;
- includes?: string[];
- excludes?: string[];
- maxCrawledLinks?: number;
- maxDepth?: number;
- limit?: number;
- generateImgAltText?: boolean;
- replaceAllPathsWithAbsolutePaths?: boolean;
- mode?: "default" | "fast"; // have a mode of some sort
- };
+ crawlerOptions?: CrawlerOptions;
pageOptions?: PageOptions;
extractorOptions?: ExtractorOptions;
concurrentRequests?: number;
@@ -74,6 +84,8 @@ export class Document {
provider?: string;
warning?: string;
+ index?: number;
+
constructor(data: Partial) {
if (!data.content) {
throw new Error("Missing required fields");
@@ -104,4 +116,12 @@ export class SearchResult {
toString(): string {
return `SearchResult(url=${this.url}, title=${this.title}, description=${this.description})`;
}
-}
\ No newline at end of file
+}
+
+export interface FireEngineResponse {
+ html: string;
+ screenshot: string;
+ pageStatusCode?: number;
+ pageError?: string;
+}
+
diff --git a/apps/api/src/lib/load-testing-example.ts b/apps/api/src/lib/load-testing-example.ts
index 6fd56fc..01b61db 100644
--- a/apps/api/src/lib/load-testing-example.ts
+++ b/apps/api/src/lib/load-testing-example.ts
@@ -1,42 +1,42 @@
-import { scrapWithFireEngine } from "../../src/scraper/WebScraper/single_url";
+// import { scrapWithFireEngine } from "../../src/scraper/WebScraper/single_url";
-const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+// const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
-const scrapInBatches = async (
- urls: string[],
- batchSize: number,
- delayMs: number
-) => {
- let successCount = 0;
- let errorCount = 0;
+// const scrapInBatches = async (
+// urls: string[],
+// batchSize: number,
+// delayMs: number
+// ) => {
+// let successCount = 0;
+// let errorCount = 0;
- for (let i = 0; i < urls.length; i += batchSize) {
- const batch = urls
- .slice(i, i + batchSize)
- .map((url) => scrapWithFireEngine(url));
- try {
- const results = await Promise.all(batch);
- results.forEach((data, index) => {
- if (data.trim() === "") {
- errorCount++;
- } else {
- successCount++;
- console.log(
- `Scraping result ${i + index + 1}:`,
- data.trim().substring(0, 20) + "..."
- );
- }
- });
- } catch (error) {
- console.error("Error during scraping:", error);
- }
- await delay(delayMs);
- }
+// for (let i = 0; i < urls.length; i += batchSize) {
+// const batch = urls
+// .slice(i, i + batchSize)
+// .map((url) => scrapWithFireEngine(url));
+// try {
+// const results = await Promise.all(batch);
+// results.forEach((data, index) => {
+// if (data.trim() === "") {
+// errorCount++;
+// } else {
+// successCount++;
+// console.log(
+// `Scraping result ${i + index + 1}:`,
+// data.trim().substring(0, 20) + "..."
+// );
+// }
+// });
+// } catch (error) {
+// console.error("Error during scraping:", error);
+// }
+// await delay(delayMs);
+// }
- console.log(`Total successful scrapes: ${successCount}`);
- console.log(`Total errored scrapes: ${errorCount}`);
-};
-function run() {
- const urls = Array.from({ length: 200 }, () => "https://scrapethissite.com");
- scrapInBatches(urls, 10, 1000);
-}
+// console.log(`Total successful scrapes: ${successCount}`);
+// console.log(`Total errored scrapes: ${errorCount}`);
+// };
+// function run() {
+// const urls = Array.from({ length: 200 }, () => "https://scrapethissite.com");
+// scrapInBatches(urls, 10, 1000);
+// }
diff --git a/apps/api/src/main/runWebScraper.ts b/apps/api/src/main/runWebScraper.ts
index 632d110..dee89bc 100644
--- a/apps/api/src/main/runWebScraper.ts
+++ b/apps/api/src/main/runWebScraper.ts
@@ -19,6 +19,9 @@ export async function startWebScraperPipeline({
inProgress: (progress) => {
if (progress.currentDocument) {
partialDocs.push(progress.currentDocument);
+ if (partialDocs.length > 50) {
+ partialDocs = partialDocs.slice(-50);
+ }
job.progress({ ...progress, partialDocs: partialDocs });
}
},
diff --git a/apps/api/src/scraper/WebScraper/crawler.ts b/apps/api/src/scraper/WebScraper/crawler.ts
index 7827620..ba5e003 100644
--- a/apps/api/src/scraper/WebScraper/crawler.ts
+++ b/apps/api/src/scraper/WebScraper/crawler.ts
@@ -3,7 +3,7 @@ import cheerio, { load } from "cheerio";
import { URL } from "url";
import { getLinksFromSitemap } from "./sitemap";
import async from "async";
-import { Progress } from "../../lib/entities";
+import { CrawlerOptions, PageOptions, Progress } from "../../lib/entities";
import { scrapSingleUrl, scrapWithScrapingBee } from "./single_url";
import robotsParser from "robots-parser";
@@ -20,15 +20,17 @@ export class WebCrawler {
private robotsTxtUrl: string;
private robots: any;
private generateImgAltText: boolean;
+ private allowBackwardCrawling: boolean;
constructor({
initialUrl,
includes,
excludes,
- maxCrawledLinks,
+ maxCrawledLinks = 10000,
limit = 10000,
generateImgAltText = false,
maxCrawledDepth = 10,
+ allowBackwardCrawling = false
}: {
initialUrl: string;
includes?: string[];
@@ -37,6 +39,7 @@ export class WebCrawler {
limit?: number;
generateImgAltText?: boolean;
maxCrawledDepth?: number;
+ allowBackwardCrawling?: boolean;
}) {
this.initialUrl = initialUrl;
this.baseUrl = new URL(initialUrl).origin;
@@ -49,6 +52,7 @@ export class WebCrawler {
this.maxCrawledLinks = maxCrawledLinks ?? limit;
this.maxCrawledDepth = maxCrawledDepth ?? 10;
this.generateImgAltText = generateImgAltText ?? false;
+ this.allowBackwardCrawling = allowBackwardCrawling ?? false;
}
private filterLinks(sitemapLinks: string[], limit: number, maxDepth: number): string[] {
@@ -90,10 +94,16 @@ export class WebCrawler {
const linkHostname = normalizedLink.hostname.replace(/^www\./, '');
// Ensure the protocol and hostname match, and the path starts with the initial URL's path
- if (linkHostname !== initialHostname || !normalizedLink.pathname.startsWith(normalizedInitialUrl.pathname)) {
+ if (linkHostname !== initialHostname) {
return false;
}
+ if (!this.allowBackwardCrawling) {
+ if (!normalizedLink.pathname.startsWith(normalizedInitialUrl.pathname)) {
+ return false;
+ }
+ }
+
const isAllowed = this.robots.isAllowed(link, "FireCrawlAgent") ?? true;
// Check if the link is disallowed by robots.txt
if (!isAllowed) {
@@ -108,6 +118,8 @@ export class WebCrawler {
public async start(
inProgress?: (progress: Progress) => void,
+ pageOptions?: PageOptions,
+ crawlerOptions?: CrawlerOptions,
concurrencyLimit: number = 5,
limit: number = 10000,
maxDepth: number = 10
@@ -122,17 +134,21 @@ export class WebCrawler {
}
- const sitemapLinks = await this.tryFetchSitemapLinks(this.initialUrl);
- if (sitemapLinks.length > 0) {
- let filteredLinks = this.filterLinks(sitemapLinks, limit, maxDepth);
- return filteredLinks.map(link => ({ url: link, html: "" }));
+ if(!crawlerOptions?.ignoreSitemap){
+ const sitemapLinks = await this.tryFetchSitemapLinks(this.initialUrl);
+ if (sitemapLinks.length > 0) {
+ let filteredLinks = this.filterLinks(sitemapLinks, limit, maxDepth);
+ return filteredLinks.map(link => ({ url: link, html: "" }));
+ }
}
const urls = await this.crawlUrls(
[this.initialUrl],
+ pageOptions,
concurrencyLimit,
inProgress
);
+
if (
urls.length === 0 &&
this.filterLinks([this.initialUrl], limit, this.maxCrawledDepth).length > 0
@@ -140,7 +156,6 @@ export class WebCrawler {
return [{ url: this.initialUrl, html: "" }];
}
-
// make sure to run include exclude here again
const filteredUrls = this.filterLinks(urls.map(urlObj => urlObj.url), limit, this.maxCrawledDepth);
return filteredUrls.map(url => ({ url, html: urls.find(urlObj => urlObj.url === url)?.html || "" }));
@@ -148,17 +163,18 @@ export class WebCrawler {
private async crawlUrls(
urls: string[],
+ pageOptions: PageOptions,
concurrencyLimit: number,
inProgress?: (progress: Progress) => void,
): Promise<{ url: string, html: string }[]> {
const queue = async.queue(async (task: string, callback) => {
- if (this.crawledUrls.size >= this.maxCrawledLinks) {
+ if (this.crawledUrls.size >= Math.min(this.maxCrawledLinks, this.limit)) {
if (callback && typeof callback === "function") {
callback();
}
return;
}
- const newUrls = await this.crawl(task);
+ const newUrls = await this.crawl(task, pageOptions);
// add the initial url if not already added
// if (this.visited.size === 1) {
// let normalizedInitial = this.initialUrl;
@@ -176,19 +192,19 @@ export class WebCrawler {
if (inProgress && newUrls.length > 0) {
inProgress({
current: this.crawledUrls.size,
- total: this.maxCrawledLinks,
+ total: Math.min(this.maxCrawledLinks, this.limit),
status: "SCRAPING",
currentDocumentUrl: newUrls[newUrls.length - 1].url,
});
} else if (inProgress) {
inProgress({
current: this.crawledUrls.size,
- total: this.maxCrawledLinks,
+ total: Math.min(this.maxCrawledLinks, this.limit),
status: "SCRAPING",
currentDocumentUrl: task,
});
}
- await this.crawlUrls(newUrls.map((p) => p.url), concurrencyLimit, inProgress);
+ await this.crawlUrls(newUrls.map((p) => p.url), pageOptions, concurrencyLimit, inProgress);
if (callback && typeof callback === "function") {
callback();
}
@@ -207,46 +223,49 @@ export class WebCrawler {
return Array.from(this.crawledUrls.entries()).map(([url, html]) => ({ url, html }));
}
- async crawl(url: string): Promise<{url: string, html: string}[]> {
- if (this.visited.has(url) || !this.robots.isAllowed(url, "FireCrawlAgent")){
+ async crawl(url: string, pageOptions: PageOptions): Promise<{url: string, html: string, pageStatusCode?: number, pageError?: string}[]> {
+ const normalizedUrl = this.normalizeCrawlUrl(url);
+ if (this.visited.has(normalizedUrl) || !this.robots.isAllowed(url, "FireCrawlAgent")) {
return [];
}
- this.visited.add(url);
-
+ this.visited.add(normalizedUrl);
if (!url.startsWith("http")) {
url = "https://" + url;
-
}
if (url.endsWith("/")) {
url = url.slice(0, -1);
-
}
-
+
if (this.isFile(url) || this.isSocialMediaOrEmail(url)) {
return [];
}
try {
- let content : string = "";
+ let content: string = "";
+ let pageStatusCode: number;
+ let pageError: string | undefined = undefined;
+
// If it is the first link, fetch with single url
if (this.visited.size === 1) {
- const page = await scrapSingleUrl(url, {includeHtml: true});
- content = page.html ?? ""
+ const page = await scrapSingleUrl(url, { ...pageOptions, includeHtml: true });
+ content = page.html ?? "";
+ pageStatusCode = page.metadata?.pageStatusCode;
+ pageError = page.metadata?.pageError || undefined;
} else {
const response = await axios.get(url);
content = response.data ?? "";
+ pageStatusCode = response.status;
+ pageError = response.statusText != "OK" ? response.statusText : undefined;
}
const $ = load(content);
- let links: {url: string, html: string}[] = [];
+ let links: { url: string, html: string, pageStatusCode?: number, pageError?: string }[] = [];
// Add the initial URL to the list of links
- if(this.visited.size === 1)
- {
- links.push({url, html: content});
+ if (this.visited.size === 1) {
+ links.push({ url, html: content, pageStatusCode, pageError });
}
-
$("a").each((_, element) => {
const href = $(element).attr("href");
if (href) {
@@ -254,32 +273,43 @@ export class WebCrawler {
if (!href.startsWith("http")) {
fullUrl = new URL(href, this.baseUrl).toString();
}
- const url = new URL(fullUrl);
- const path = url.pathname;
+ const urlObj = new URL(fullUrl);
+ const path = urlObj.pathname;
if (
this.isInternalLink(fullUrl) &&
this.matchesPattern(fullUrl) &&
this.noSections(fullUrl) &&
- this.matchesIncludes(path) &&
+ // The idea here to comment this out is to allow wider website coverage as we filter this anyway afterwards
+ // this.matchesIncludes(path) &&
!this.matchesExcludes(path) &&
this.robots.isAllowed(fullUrl, "FireCrawlAgent")
) {
- links.push({url: fullUrl, html: content});
+ links.push({ url: fullUrl, html: content, pageStatusCode, pageError });
}
}
});
- if(this.visited.size === 1){
+ if (this.visited.size === 1) {
return links;
}
// Create a new list to return to avoid modifying the visited list
- return links.filter((link) => !this.visited.has(link.url));
+ return links.filter((link) => !this.visited.has(this.normalizeCrawlUrl(link.url)));
} catch (error) {
return [];
}
}
+ private normalizeCrawlUrl(url: string): string {
+ try{
+ const urlObj = new URL(url);
+ urlObj.searchParams.sort(); // Sort query parameters to normalize
+ return urlObj.toString();
+ } catch (error) {
+ return url;
+ }
+ }
+
private matchesIncludes(url: string): boolean {
if (this.includes.length === 0 || this.includes[0] == "") return true;
return this.includes.some((pattern) => new RegExp(pattern).test(url));
@@ -324,6 +354,12 @@ export class WebCrawler {
// ".docx",
".xlsx",
".xml",
+ ".avi",
+ ".flv",
+ ".woff",
+ ".ttf",
+ ".woff2",
+ ".webp"
];
return fileExtensions.some((ext) => url.endsWith(ext));
}
@@ -382,7 +418,6 @@ export class WebCrawler {
// Normalize and check if the URL is present in any of the sitemaps
const normalizedUrl = normalizeUrl(url);
-
const normalizedSitemapLinks = sitemapLinks.map(link => normalizeUrl(link));
// has to be greater than 0 to avoid adding the initial URL to the sitemap links, and preventing crawler to crawl
diff --git a/apps/api/src/scraper/WebScraper/custom/handleCustomScraping.ts b/apps/api/src/scraper/WebScraper/custom/handleCustomScraping.ts
new file mode 100644
index 0000000..081150b
--- /dev/null
+++ b/apps/api/src/scraper/WebScraper/custom/handleCustomScraping.ts
@@ -0,0 +1,51 @@
+export async function handleCustomScraping(
+ text: string,
+ url: string
+): Promise<{ scraper: string; url: string; waitAfterLoad?: number, pageOptions?: { scrollXPaths?: string[] } } | null> {
+ // Check for Readme Docs special case
+ if (text.includes(' result !== null) as Document[];
@@ -159,18 +163,27 @@ export class WebScraperDataProvider {
inProgress?: (progress: Progress) => void
): Promise {
+ const pathSplits = new URL(this.urls[0]).pathname.split('/');
+ const baseURLDepth = pathSplits.length - (pathSplits[0].length === 0 && pathSplits[pathSplits.length - 1].length === 0 ? 1 : 0);
+ const adjustedMaxDepth = this.maxCrawledDepth + baseURLDepth;
+
const crawler = new WebCrawler({
initialUrl: this.urls[0],
includes: this.includes,
excludes: this.excludes,
maxCrawledLinks: this.maxCrawledLinks,
- maxCrawledDepth: this.maxCrawledDepth,
+ maxCrawledDepth: adjustedMaxDepth,
limit: this.limit,
generateImgAltText: this.generateImgAltText,
+ allowBackwardCrawling: this.allowBackwardCrawling,
});
let links = await crawler.start(
inProgress,
+ this.pageOptions,
+ {
+ ignoreSitemap: this.ignoreSitemap,
+ },
5,
this.limit,
this.maxCrawledDepth
@@ -213,6 +226,7 @@ export class WebScraperDataProvider {
return this.returnOnlyUrlsResponse(links, inProgress);
}
+
let documents = await this.processLinks(links, inProgress);
return this.cacheAndFinalizeDocuments(documents, links);
}
@@ -231,7 +245,7 @@ export class WebScraperDataProvider {
content: "",
html: this.pageOptions?.includeHtml ? "" : undefined,
markdown: "",
- metadata: { sourceURL: url },
+ metadata: { sourceURL: url, pageStatusCode: 200 },
}));
}
@@ -270,10 +284,10 @@ export class WebScraperDataProvider {
private async fetchPdfDocuments(pdfLinks: string[]): Promise {
return Promise.all(
pdfLinks.map(async (pdfLink) => {
- const pdfContent = await fetchAndProcessPdf(pdfLink);
+ const { content, pageStatusCode, pageError } = await fetchAndProcessPdf(pdfLink, this.pageOptions.parsePDF);
return {
- content: pdfContent,
- metadata: { sourceURL: pdfLink },
+ content: content,
+ metadata: { sourceURL: pdfLink, pageStatusCode, pageError },
provider: "web-scraper",
};
})
@@ -282,10 +296,10 @@ export class WebScraperDataProvider {
private async fetchDocxDocuments(docxLinks: string[]): Promise {
return Promise.all(
docxLinks.map(async (p) => {
- const docXDocument = await fetchAndProcessDocx(p);
+ const { content, pageStatusCode, pageError } = await fetchAndProcessDocx(p);
return {
- content: docXDocument,
- metadata: { sourceURL: p },
+ content,
+ metadata: { sourceURL: p, pageStatusCode, pageError },
provider: "web-scraper",
};
})
@@ -293,9 +307,10 @@ export class WebScraperDataProvider {
}
private applyPathReplacements(documents: Document[]): Document[] {
- return this.replaceAllPathsWithAbsolutePaths
- ? replacePathsWithAbsolutePaths(documents)
- : replaceImgPathsWithAbsolutePaths(documents);
+ if (this.replaceAllPathsWithAbsolutePaths) {
+ documents = replacePathsWithAbsolutePaths(documents);
+ }
+ return replaceImgPathsWithAbsolutePaths(documents);
}
private async applyImgAltText(documents: Document[]): Promise {
@@ -464,12 +479,20 @@ export class WebScraperDataProvider {
this.limit = options.crawlerOptions?.limit ?? 10000;
this.generateImgAltText =
options.crawlerOptions?.generateImgAltText ?? false;
- this.pageOptions = options.pageOptions ?? { onlyMainContent: false, includeHtml: false };
+ this.pageOptions = options.pageOptions ?? {
+ onlyMainContent: false,
+ includeHtml: false,
+ replaceAllPathsWithAbsolutePaths: false,
+ parsePDF: true,
+ removeTags: []
+ };
this.extractorOptions = options.extractorOptions ?? {mode: "markdown"}
- this.replaceAllPathsWithAbsolutePaths = options.crawlerOptions?.replaceAllPathsWithAbsolutePaths ?? false;
+ this.replaceAllPathsWithAbsolutePaths = options.crawlerOptions?.replaceAllPathsWithAbsolutePaths ?? options.pageOptions?.replaceAllPathsWithAbsolutePaths ?? false;
//! @nicolas, for some reason this was being injected and breaking everything. Don't have time to find source of the issue so adding this check
this.excludes = this.excludes.filter((item) => item !== "");
this.crawlerMode = options.crawlerOptions?.mode ?? "default";
+ this.ignoreSitemap = options.crawlerOptions?.ignoreSitemap ?? false;
+ this.allowBackwardCrawling = options.crawlerOptions?.allowBackwardCrawling ?? false;
// make sure all urls start with https://
this.urls = this.urls.map((url) => {
diff --git a/apps/api/src/scraper/WebScraper/single_url.ts b/apps/api/src/scraper/WebScraper/single_url.ts
index bd2bacb..db8f0ae 100644
--- a/apps/api/src/scraper/WebScraper/single_url.ts
+++ b/apps/api/src/scraper/WebScraper/single_url.ts
@@ -2,11 +2,13 @@ import * as cheerio from "cheerio";
import { ScrapingBeeClient } from "scrapingbee";
import { extractMetadata } from "./utils/metadata";
import dotenv from "dotenv";
-import { Document, PageOptions } from "../../lib/entities";
+import { Document, PageOptions, FireEngineResponse } from "../../lib/entities";
import { parseMarkdown } from "../../lib/html-to-markdown";
import { excludeNonMainTags } from "./utils/excludeTags";
import { urlSpecificParams } from "./utils/custom/website_params";
import { fetchAndProcessPdf } from "./utils/pdfProcessor";
+import { handleCustomScraping } from "./custom/handleCustomScraping";
+import axios from "axios";
dotenv.config();
@@ -18,6 +20,7 @@ const baseScrapers = [
"fetch",
] as const;
+const universalTimeout = 15000;
export async function generateRequestParams(
url: string,
@@ -44,131 +47,195 @@ export async function generateRequestParams(
}
export async function scrapWithFireEngine(
url: string,
+ waitFor: number = 0,
+ screenshot: boolean = false,
+ pageOptions: { scrollXPaths?: string[], parsePDF?: boolean } = { parsePDF: true },
+ headers?: Record,
options?: any
-): Promise {
+): Promise {
try {
const reqParams = await generateRequestParams(url);
- const wait_playwright = reqParams["params"]?.wait ?? 0;
+ // If the user has passed a wait parameter in the request, use that
+ const waitParam = reqParams["params"]?.wait ?? waitFor;
+ const screenshotParam = reqParams["params"]?.screenshot ?? screenshot;
+ console.log(
+ `[Fire-Engine] Scraping ${url} with wait: ${waitParam} and screenshot: ${screenshotParam}`
+ );
- const response = await fetch(process.env.FIRE_ENGINE_BETA_URL+ "/scrape", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
+ const response = await axios.post(
+ process.env.FIRE_ENGINE_BETA_URL + "/scrape",
+ {
+ url: url,
+ wait: waitParam,
+ screenshot: screenshotParam,
+ headers: headers,
+ pageOptions: pageOptions,
},
- body: JSON.stringify({ url: url, wait: wait_playwright }),
- });
+ {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ timeout: universalTimeout + waitParam
+ }
+ );
- if (!response.ok) {
+ if (response.status !== 200) {
console.error(
`[Fire-Engine] Error fetching url: ${url} with status: ${response.status}`
);
- return "";
+ return { html: "", screenshot: "", pageStatusCode: response.data?.pageStatusCode, pageError: response.data?.pageError };
}
- const contentType = response.headers['content-type'];
- if (contentType && contentType.includes('application/pdf')) {
- return fetchAndProcessPdf(url);
+ const contentType = response.headers["content-type"];
+ if (contentType && contentType.includes("application/pdf")) {
+ const { content, pageStatusCode, pageError } = await fetchAndProcessPdf(url, pageOptions?.parsePDF);
+ return { html: content, screenshot: "", pageStatusCode, pageError };
} else {
- const data = await response.json();
+ const data = response.data;
const html = data.content;
- return html ?? "";
+ const screenshot = data.screenshot;
+ return { html: html ?? "", screenshot: screenshot ?? "", pageStatusCode: data.pageStatusCode, pageError: data.pageError };
}
} catch (error) {
- console.error(`[Fire-Engine][c] Error fetching url: ${url} -> ${error}`);
- return "";
+ if (error.code === 'ECONNABORTED') {
+ console.log(`[Fire-Engine] Request timed out for ${url}`);
+ } else {
+ console.error(`[Fire-Engine][c] Error fetching url: ${url} -> ${error}`);
+ }
+ return { html: "", screenshot: "" };
}
}
export async function scrapWithScrapingBee(
url: string,
wait_browser: string = "domcontentloaded",
- timeout: number = 15000
-): Promise {
+ timeout: number = universalTimeout,
+ pageOptions: { parsePDF?: boolean } = { parsePDF: true }
+): Promise<{ content: string, pageStatusCode?: number, pageError?: string }> {
try {
const client = new ScrapingBeeClient(process.env.SCRAPING_BEE_API_KEY);
const clientParams = await generateRequestParams(
url,
wait_browser,
- timeout
+ timeout,
);
- const response = await client.get(clientParams);
+ const response = await client.get({
+ ...clientParams,
+ params: {
+ ...clientParams.params,
+ 'transparent_status_code': 'True'
+ }
+ });
+
+ const contentType = response.headers["content-type"];
+ if (contentType && contentType.includes("application/pdf")) {
+ return await fetchAndProcessPdf(url, pageOptions?.parsePDF);
- if (response.status !== 200 && response.status !== 404) {
- console.error(
- `[ScrapingBee] Error fetching url: ${url} with status code ${response.status}`
- );
- return "";
- }
-
- const contentType = response.headers['content-type'];
- if (contentType && contentType.includes('application/pdf')) {
- return fetchAndProcessPdf(url);
} else {
- const decoder = new TextDecoder();
- const text = decoder.decode(response.data);
- return text;
+ let text = "";
+ try {
+ const decoder = new TextDecoder();
+ text = decoder.decode(response.data);
+ } catch (decodeError) {
+ console.error(`[ScrapingBee][c] Error decoding response data for url: ${url} -> ${decodeError}`);
+ }
+ return { content: text, pageStatusCode: response.status, pageError: response.statusText != "OK" ? response.statusText : undefined };
}
} catch (error) {
console.error(`[ScrapingBee][c] Error fetching url: ${url} -> ${error}`);
- return "";
+ return { content: "", pageStatusCode: error.response.status, pageError: error.response.statusText };
}
}
-export async function scrapWithPlaywright(url: string): Promise {
+export async function scrapWithPlaywright(
+ url: string,
+ waitFor: number = 0,
+ headers?: Record,
+ pageOptions: { parsePDF?: boolean } = { parsePDF: true }
+): Promise<{ content: string, pageStatusCode?: number, pageError?: string }> {
try {
const reqParams = await generateRequestParams(url);
- const wait_playwright = reqParams["params"]?.wait ?? 0;
+ // If the user has passed a wait parameter in the request, use that
+ const waitParam = reqParams["params"]?.wait ?? waitFor;
- const response = await fetch(process.env.PLAYWRIGHT_MICROSERVICE_URL, {
- method: "POST",
+ const response = await axios.post(process.env.PLAYWRIGHT_MICROSERVICE_URL, {
+ url: url,
+ wait_after_load: waitParam,
+ headers: headers,
+ }, {
headers: {
"Content-Type": "application/json",
},
- body: JSON.stringify({ url: url, wait: wait_playwright }),
+ timeout: universalTimeout + waitParam, // Add waitParam to timeout to account for the wait time
+ transformResponse: [(data) => data] // Prevent axios from parsing JSON automatically
});
- if (!response.ok) {
+ if (response.status !== 200) {
console.error(
`[Playwright] Error fetching url: ${url} with status: ${response.status}`
);
- return "";
+ return { content: "", pageStatusCode: response.data?.pageStatusCode, pageError: response.data?.pageError };
}
- const contentType = response.headers['content-type'];
- if (contentType && contentType.includes('application/pdf')) {
- return fetchAndProcessPdf(url);
+ const contentType = response.headers["content-type"];
+ if (contentType && contentType.includes("application/pdf")) {
+ return await fetchAndProcessPdf(url, pageOptions?.parsePDF);
} else {
- const data = await response.json();
- const html = data.content;
- return html ?? "";
+ const textData = response.data;
+ try {
+ const data = JSON.parse(textData);
+ const html = data.content;
+ return { content: html ?? "", pageStatusCode: data.pageStatusCode, pageError: data.pageError };
+ } catch (jsonError) {
+ console.error(`[Playwright] Error parsing JSON response for url: ${url} -> ${jsonError}`);
+ return { content: "" };
+ }
}
} catch (error) {
- console.error(`[Playwright][c] Error fetching url: ${url} -> ${error}`);
- return "";
+ if (error.code === 'ECONNABORTED') {
+ console.log(`[Playwright] Request timed out for ${url}`);
+ } else {
+ console.error(`[Playwright] Error fetching url: ${url} -> ${error}`);
+ }
+ return { content: "" };
}
}
-export async function scrapWithFetch(url: string): Promise {
+export async function scrapWithFetch(
+ url: string,
+ pageOptions: { parsePDF?: boolean } = { parsePDF: true }
+): Promise<{ content: string, pageStatusCode?: number, pageError?: string }> {
try {
- const response = await fetch(url);
- if (!response.ok) {
+ const response = await axios.get(url, {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ timeout: universalTimeout,
+ transformResponse: [(data) => data] // Prevent axios from parsing JSON automatically
+ });
+
+ if (response.status !== 200) {
console.error(
- `[Fetch] Error fetching url: ${url} with status: ${response.status}`
+ `[Axios] Error fetching url: ${url} with status: ${response.status}`
);
- return "";
+ return { content: "", pageStatusCode: response.status, pageError: response.statusText };
}
- const contentType = response.headers['content-type'];
- if (contentType && contentType.includes('application/pdf')) {
- return fetchAndProcessPdf(url);
+ const contentType = response.headers["content-type"];
+ if (contentType && contentType.includes("application/pdf")) {
+ return await fetchAndProcessPdf(url, pageOptions?.parsePDF);
} else {
- const text = await response.text();
- return text;
+ const text = response.data;
+ return { content: text, pageStatusCode: 200 };
}
} catch (error) {
- console.error(`[Fetch][c] Error fetching url: ${url} -> ${error}`);
- return "";
+ if (error.code === 'ECONNABORTED') {
+ console.log(`[Axios] Request timed out for ${url}`);
+ } else {
+ console.error(`[Axios] Error fetching url: ${url} -> ${error}`);
+ }
+ return { content: "" };
}
}
@@ -178,8 +245,13 @@ export async function scrapWithFetch(url: string): Promise {
* @param defaultScraper The default scraper to use if the URL does not have a specific scraper order defined
* @returns The order of scrapers to be used for scraping a URL
*/
-function getScrapingFallbackOrder(defaultScraper?: string) {
- const availableScrapers = baseScrapers.filter(scraper => {
+function getScrapingFallbackOrder(
+ defaultScraper?: string,
+ isWaitPresent: boolean = false,
+ isScreenshotPresent: boolean = false,
+ isHeadersPresent: boolean = false
+) {
+ const availableScrapers = baseScrapers.filter((scraper) => {
switch (scraper) {
case "scrapingBee":
case "scrapingBeeLoad":
@@ -193,16 +265,50 @@ function getScrapingFallbackOrder(defaultScraper?: string) {
}
});
- const defaultOrder = ["fire-engine", "scrapingBee", "playwright", "scrapingBeeLoad", "fetch"];
- const filteredDefaultOrder = defaultOrder.filter((scraper: typeof baseScrapers[number]) => availableScrapers.includes(scraper));
- const uniqueScrapers = new Set(defaultScraper ? [defaultScraper, ...filteredDefaultOrder, ...availableScrapers] : [...filteredDefaultOrder, ...availableScrapers]);
+ let defaultOrder = [
+ "scrapingBee",
+ "fire-engine",
+ "playwright",
+ "scrapingBeeLoad",
+ "fetch",
+ ];
+
+ if (isWaitPresent || isScreenshotPresent || isHeadersPresent) {
+ defaultOrder = [
+ "fire-engine",
+ "playwright",
+ ...defaultOrder.filter(
+ (scraper) => scraper !== "fire-engine" && scraper !== "playwright"
+ ),
+ ];
+ }
+
+ const filteredDefaultOrder = defaultOrder.filter(
+ (scraper: (typeof baseScrapers)[number]) =>
+ availableScrapers.includes(scraper)
+ );
+ const uniqueScrapers = new Set(
+ defaultScraper
+ ? [defaultScraper, ...filteredDefaultOrder, ...availableScrapers]
+ : [...filteredDefaultOrder, ...availableScrapers]
+ );
+
const scrapersInOrder = Array.from(uniqueScrapers);
- return scrapersInOrder as typeof baseScrapers[number][];
+ return scrapersInOrder as (typeof baseScrapers)[number][];
}
+
+
+
export async function scrapSingleUrl(
urlToScrap: string,
- pageOptions: PageOptions = { onlyMainContent: true, includeHtml: false },
+ pageOptions: PageOptions = {
+ onlyMainContent: true,
+ includeHtml: false,
+ waitFor: 0,
+ screenshot: false,
+ headers: undefined
+ },
existingHtml: string = ""
): Promise {
urlToScrap = urlToScrap.trim();
@@ -210,6 +316,19 @@ export async function scrapSingleUrl(
const removeUnwantedElements = (html: string, pageOptions: PageOptions) => {
const soup = cheerio.load(html);
soup("script, style, iframe, noscript, meta, head").remove();
+
+ if (pageOptions.removeTags) {
+ if (typeof pageOptions.removeTags === 'string') {
+ pageOptions.removeTags.split(',').forEach((tag) => {
+ soup(tag.trim()).remove();
+ });
+ } else if (Array.isArray(pageOptions.removeTags)) {
+ pageOptions.removeTags.forEach((tag) => {
+ soup(tag).remove();
+ });
+ }
+ }
+
if (pageOptions.onlyMainContent) {
// remove any other tags that are not in the main content
excludeNonMainTags.forEach((tag) => {
@@ -221,46 +340,100 @@ export async function scrapSingleUrl(
const attemptScraping = async (
url: string,
- method: typeof baseScrapers[number]
+ method: (typeof baseScrapers)[number]
) => {
- let text = "";
+ let scraperResponse: { text: string, screenshot: string, metadata: { pageStatusCode?: number, pageError?: string | null } } = { text: "", screenshot: "", metadata: {} };
+ let screenshot = "";
switch (method) {
case "fire-engine":
if (process.env.FIRE_ENGINE_BETA_URL) {
- text = await scrapWithFireEngine(url);
+ console.log(`Scraping ${url} with Fire Engine`);
+ const response = await scrapWithFireEngine(
+ url,
+ pageOptions.waitFor,
+ pageOptions.screenshot,
+ pageOptions.headers
+ );
+ scraperResponse.text = response.html;
+ scraperResponse.screenshot = response.screenshot;
+ scraperResponse.metadata.pageStatusCode = response.pageStatusCode;
+ scraperResponse.metadata.pageError = response.pageError;
}
break;
case "scrapingBee":
if (process.env.SCRAPING_BEE_API_KEY) {
- text = await scrapWithScrapingBee(
+ const response = await scrapWithScrapingBee(
url,
"domcontentloaded",
pageOptions.fallback === false ? 7000 : 15000
);
+ scraperResponse.text = response.content;
+ scraperResponse.metadata.pageStatusCode = response.pageStatusCode;
+ scraperResponse.metadata.pageError = response.pageError;
}
break;
case "playwright":
if (process.env.PLAYWRIGHT_MICROSERVICE_URL) {
- text = await scrapWithPlaywright(url);
+ const response = await scrapWithPlaywright(url, pageOptions.waitFor, pageOptions.headers);
+ scraperResponse.text = response.content;
+ scraperResponse.metadata.pageStatusCode = response.pageStatusCode;
+ scraperResponse.metadata.pageError = response.pageError;
}
break;
case "scrapingBeeLoad":
if (process.env.SCRAPING_BEE_API_KEY) {
- text = await scrapWithScrapingBee(url, "networkidle2");
+ const response = await scrapWithScrapingBee(url, "networkidle2");
+ scraperResponse.text = response.content;
+ scraperResponse.metadata.pageStatusCode = response.pageStatusCode;
+ scraperResponse.metadata.pageError = response.pageError;
}
break;
case "fetch":
- text = await scrapWithFetch(url);
+ const response = await scrapWithFetch(url);
+ scraperResponse.text = response.content;
+ scraperResponse.metadata.pageStatusCode = response.pageStatusCode;
+ scraperResponse.metadata.pageError = response.pageError;
break;
}
- //* TODO: add an optional to return markdown or structured/extracted content
- let cleanedHtml = removeUnwantedElements(text, pageOptions);
+ let customScrapedContent : FireEngineResponse | null = null;
- return [await parseMarkdown(cleanedHtml), text];
+ // Check for custom scraping conditions
+ const customScraperResult = await handleCustomScraping(scraperResponse.text, url);
+
+ if (customScraperResult){
+ switch (customScraperResult.scraper) {
+ case "fire-engine":
+ customScrapedContent = await scrapWithFireEngine(customScraperResult.url, customScraperResult.waitAfterLoad, false, customScraperResult.pageOptions)
+ if (screenshot) {
+ customScrapedContent.screenshot = screenshot;
+ }
+ break;
+ case "pdf":
+ const { content, pageStatusCode, pageError } = await fetchAndProcessPdf(customScraperResult.url, pageOptions?.parsePDF);
+ customScrapedContent = { html: content, screenshot, pageStatusCode, pageError }
+ break;
+ }
+ }
+
+ if (customScrapedContent) {
+ scraperResponse.text = customScrapedContent.html;
+ screenshot = customScrapedContent.screenshot;
+ }
+
+ //* TODO: add an optional to return markdown or structured/extracted content
+ let cleanedHtml = removeUnwantedElements(scraperResponse.text, pageOptions);
+
+ return {
+ text: await parseMarkdown(cleanedHtml),
+ html: scraperResponse.text,
+ screenshot: scraperResponse.screenshot,
+ pageStatusCode: scraperResponse.metadata.pageStatusCode,
+ pageError: scraperResponse.metadata.pageError || undefined
+ };
};
+ let { text, html, screenshot, pageStatusCode, pageError } = { text: "", html: "", screenshot: "", pageStatusCode: 200, pageError: undefined };
try {
- let [text, html] = ["", ""];
let urlKey = urlToScrap;
try {
urlKey = new URL(urlToScrap).hostname.replace(/^www\./, "");
@@ -268,7 +441,12 @@ export async function scrapSingleUrl(
console.error(`Invalid URL key, trying: ${urlToScrap}`);
}
const defaultScraper = urlSpecificParams[urlKey]?.defaultScraper ?? "";
- const scrapersInOrder = getScrapingFallbackOrder(defaultScraper)
+ const scrapersInOrder = getScrapingFallbackOrder(
+ defaultScraper,
+ pageOptions && pageOptions.waitFor && pageOptions.waitFor > 0,
+ pageOptions && pageOptions.screenshot && pageOptions.screenshot === true,
+ pageOptions && pageOptions.headers && pageOptions.headers !== undefined
+ );
for (const scraper of scrapersInOrder) {
// If exists text coming from crawler, use it
@@ -278,8 +456,21 @@ export async function scrapSingleUrl(
html = existingHtml;
break;
}
- [text, html] = await attemptScraping(urlToScrap, scraper);
+
+ const attempt = await attemptScraping(urlToScrap, scraper);
+ text = attempt.text ?? '';
+ html = attempt.html ?? '';
+ screenshot = attempt.screenshot ?? '';
+ if (attempt.pageStatusCode) {
+ pageStatusCode = attempt.pageStatusCode;
+ }
+ if (attempt.pageError) {
+ pageError = attempt.pageError;
+ }
+
+
if (text && text.trim().length >= 100) break;
+ if (pageStatusCode && pageStatusCode == 404) break;
const nextScraperIndex = scrapersInOrder.indexOf(scraper) + 1;
if (nextScraperIndex < scrapersInOrder.length) {
console.info(`Falling back to ${scrapersInOrder[nextScraperIndex]}`);
@@ -292,12 +483,34 @@ export async function scrapSingleUrl(
const soup = cheerio.load(html);
const metadata = extractMetadata(soup, urlToScrap);
- const document: Document = {
- content: text,
- markdown: text,
- html: pageOptions.includeHtml ? html : undefined,
- metadata: { ...metadata, sourceURL: urlToScrap },
- };
+
+ let document: Document;
+ if (screenshot && screenshot.length > 0) {
+ document = {
+ content: text,
+ markdown: text,
+ html: pageOptions.includeHtml ? html : undefined,
+ metadata: {
+ ...metadata,
+ screenshot: screenshot,
+ sourceURL: urlToScrap,
+ pageStatusCode: pageStatusCode,
+ pageError: pageError
+ },
+ };
+ } else {
+ document = {
+ content: text,
+ markdown: text,
+ html: pageOptions.includeHtml ? html : undefined,
+ metadata: {
+ ...metadata,
+ sourceURL: urlToScrap,
+ pageStatusCode: pageStatusCode,
+ pageError: pageError
+ },
+ };
+ }
return document;
} catch (error) {
@@ -306,7 +519,11 @@ export async function scrapSingleUrl(
content: "",
markdown: "",
html: "",
- metadata: { sourceURL: urlToScrap },
+ metadata: {
+ sourceURL: urlToScrap,
+ pageStatusCode: pageStatusCode,
+ pageError: pageError
+ },
} as Document;
}
}
diff --git a/apps/api/src/scraper/WebScraper/sitemap.ts b/apps/api/src/scraper/WebScraper/sitemap.ts
index 0ac4338..c6dbf11 100644
--- a/apps/api/src/scraper/WebScraper/sitemap.ts
+++ b/apps/api/src/scraper/WebScraper/sitemap.ts
@@ -12,6 +12,7 @@ export async function getLinksFromSitemap(
content = response.data;
} catch (error) {
console.error(`Request failed for ${sitemapUrl}: ${error}`);
+
return allUrls;
}
diff --git a/apps/api/src/scraper/WebScraper/utils/__tests__/docxProcessor.test.ts b/apps/api/src/scraper/WebScraper/utils/__tests__/docxProcessor.test.ts
index e018ffa..53237ef 100644
--- a/apps/api/src/scraper/WebScraper/utils/__tests__/docxProcessor.test.ts
+++ b/apps/api/src/scraper/WebScraper/utils/__tests__/docxProcessor.test.ts
@@ -3,11 +3,13 @@ import * as docxProcessor from "../docxProcessor";
describe("DOCX Processing Module - Integration Test", () => {
it("should correctly process a simple DOCX file without the LLAMAPARSE_API_KEY", async () => {
delete process.env.LLAMAPARSE_API_KEY;
- const docxContent = await docxProcessor.fetchAndProcessDocx(
+ const { content, pageStatusCode, pageError } = await docxProcessor.fetchAndProcessDocx(
"https://nvca.org/wp-content/uploads/2019/06/NVCA-Model-Document-Stock-Purchase-Agreement.docx"
);
- expect(docxContent.trim()).toContain(
+ expect(content.trim()).toContain(
"SERIES A PREFERRED STOCK PURCHASE AGREEMENT"
);
+ expect(pageStatusCode).toBe(200);
+ expect(pageError).toBeUndefined();
});
});
diff --git a/apps/api/src/scraper/WebScraper/utils/__tests__/pdfProcessor.test.ts b/apps/api/src/scraper/WebScraper/utils/__tests__/pdfProcessor.test.ts
index f14c8d4..55930f2 100644
--- a/apps/api/src/scraper/WebScraper/utils/__tests__/pdfProcessor.test.ts
+++ b/apps/api/src/scraper/WebScraper/utils/__tests__/pdfProcessor.test.ts
@@ -3,8 +3,10 @@ import * as pdfProcessor from '../pdfProcessor';
describe('PDF Processing Module - Integration Test', () => {
it('should correctly process a simple PDF file without the LLAMAPARSE_API_KEY', async () => {
delete process.env.LLAMAPARSE_API_KEY;
- const pdfContent = await pdfProcessor.fetchAndProcessPdf('https://s3.us-east-1.amazonaws.com/storage.mendable.ai/rafa-testing/test%20%281%29.pdf');
- expect(pdfContent.trim()).toEqual("Dummy PDF file");
+ const { content, pageStatusCode, pageError } = await pdfProcessor.fetchAndProcessPdf('https://s3.us-east-1.amazonaws.com/storage.mendable.ai/rafa-testing/test%20%281%29.pdf', true);
+ expect(content.trim()).toEqual("Dummy PDF file");
+ expect(pageStatusCode).toEqual(200);
+ expect(pageError).toBeUndefined();
});
// We're hitting the LLAMAPARSE rate limit 🫠
diff --git a/apps/api/src/scraper/WebScraper/utils/__tests__/replacePaths.test.ts b/apps/api/src/scraper/WebScraper/utils/__tests__/replacePaths.test.ts
index aae567c..e201926 100644
--- a/apps/api/src/scraper/WebScraper/utils/__tests__/replacePaths.test.ts
+++ b/apps/api/src/scraper/WebScraper/utils/__tests__/replacePaths.test.ts
@@ -6,12 +6,14 @@ describe('replacePaths', () => {
it('should replace relative paths with absolute paths', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'This is a [link](/path/to/resource) and an image ![alt text](/path/to/image.jpg).'
+ content: 'This is a [link](/path/to/resource).',
+ markdown: 'This is a [link](/path/to/resource).'
}];
const expectedDocuments: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'This is a [link](https://example.com/path/to/resource) and an image ![alt text](https://example.com/path/to/image.jpg).'
+ content: 'This is a [link](https://example.com/path/to/resource).',
+ markdown: 'This is a [link](https://example.com/path/to/resource).'
}];
const result = replacePathsWithAbsolutePaths(documents);
@@ -21,7 +23,8 @@ describe('replacePaths', () => {
it('should not alter absolute URLs', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'This is an [external link](https://external.com/path) and an image ![alt text](https://example.com/path/to/image.jpg).'
+ content: 'This is an [external link](https://external.com/path).',
+ markdown: 'This is an [external link](https://external.com/path).'
}];
const result = replacePathsWithAbsolutePaths(documents);
@@ -31,7 +34,8 @@ describe('replacePaths', () => {
it('should not alter data URLs for images', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'This is an image: ![alt text](data:image/png;base64,ABC123==).'
+ content: 'This is an image: ![alt text](data:image/png;base64,ABC123==).',
+ markdown: 'This is an image: ![alt text](data:image/png;base64,ABC123==).'
}];
const result = replacePathsWithAbsolutePaths(documents);
@@ -41,12 +45,14 @@ describe('replacePaths', () => {
it('should handle multiple links and images correctly', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Here are two links: [link1](/path1) and [link2](/path2), and two images: ![img1](/img1.jpg) ![img2](/img2.jpg).'
+ content: 'Here are two links: [link1](/path1) and [link2](/path2).',
+ markdown: 'Here are two links: [link1](/path1) and [link2](/path2).'
}];
const expectedDocuments: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Here are two links: [link1](https://example.com/path1) and [link2](https://example.com/path2), and two images: ![img1](https://example.com/img1.jpg) ![img2](https://example.com/img2.jpg).'
+ content: 'Here are two links: [link1](https://example.com/path1) and [link2](https://example.com/path2).',
+ markdown: 'Here are two links: [link1](https://example.com/path1) and [link2](https://example.com/path2).'
}];
const result = replacePathsWithAbsolutePaths(documents);
@@ -56,12 +62,14 @@ describe('replacePaths', () => {
it('should correctly handle a mix of absolute and relative paths', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Mixed paths: [relative](/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).'
+ content: 'Mixed paths: [relative](/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).',
+ markdown: 'Mixed paths: [relative](/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).'
}];
const expectedDocuments: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Mixed paths: [relative](https://example.com/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).'
+ content: 'Mixed paths: [relative](https://example.com/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).',
+ markdown: 'Mixed paths: [relative](https://example.com/path), [absolute](https://example.com/path), and [data image](data:image/png;base64,ABC123==).'
}];
const result = replacePathsWithAbsolutePaths(documents);
@@ -74,12 +82,14 @@ describe('replacePaths', () => {
it('should replace relative image paths with absolute paths', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Here is an image: ![alt text](/path/to/image.jpg).'
+ content: 'Here is an image: ![alt text](/path/to/image.jpg).',
+ markdown: 'Here is an image: ![alt text](/path/to/image.jpg).'
}];
const expectedDocuments: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Here is an image: ![alt text](https://example.com/path/to/image.jpg).'
+ content: 'Here is an image: ![alt text](https://example.com/path/to/image.jpg).',
+ markdown: 'Here is an image: ![alt text](https://example.com/path/to/image.jpg).'
}];
const result = replaceImgPathsWithAbsolutePaths(documents);
@@ -89,7 +99,8 @@ describe('replacePaths', () => {
it('should not alter data:image URLs', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'An image with a data URL: ![alt text](data:image/png;base64,ABC123==).'
+ content: 'An image with a data URL: ![alt text](data:image/png;base64,ABC123==).',
+ markdown: 'An image with a data URL: ![alt text](data:image/png;base4,ABC123==).'
}];
const result = replaceImgPathsWithAbsolutePaths(documents);
@@ -99,12 +110,14 @@ describe('replacePaths', () => {
it('should handle multiple images with a mix of data and relative URLs', () => {
const documents: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Multiple images: ![img1](/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](/img3.jpg).'
+ content: 'Multiple images: ![img1](/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](/img3.jpg).',
+ markdown: 'Multiple images: ![img1](/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](/img3.jpg).'
}];
const expectedDocuments: Document[] = [{
metadata: { sourceURL: 'https://example.com' },
- content: 'Multiple images: ![img1](https://example.com/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](https://example.com/img3.jpg).'
+ content: 'Multiple images: ![img1](https://example.com/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](https://example.com/img3.jpg).',
+ markdown: 'Multiple images: ![img1](https://example.com/img1.jpg) ![img2](data:image/png;base64,ABC123==) ![img3](https://example.com/img3.jpg).'
}];
const result = replaceImgPathsWithAbsolutePaths(documents);
diff --git a/apps/api/src/scraper/WebScraper/utils/__tests__/socialBlockList.test.ts b/apps/api/src/scraper/WebScraper/utils/__tests__/socialBlockList.test.ts
new file mode 100644
index 0000000..4449285
--- /dev/null
+++ b/apps/api/src/scraper/WebScraper/utils/__tests__/socialBlockList.test.ts
@@ -0,0 +1,66 @@
+import { isUrlBlocked } from '../blocklist';
+
+describe('isUrlBlocked', () => {
+ it('should return true for blocked social media URLs', () => {
+ const blockedUrls = [
+ 'https://www.facebook.com',
+ 'https://twitter.com/someuser',
+ 'https://instagram.com/someuser',
+ 'https://www.linkedin.com/in/someuser',
+ 'https://pinterest.com/someuser',
+ 'https://snapchat.com/someuser',
+ 'https://tiktok.com/@someuser',
+ 'https://reddit.com/r/somesubreddit',
+ 'https://flickr.com/photos/someuser',
+ 'https://whatsapp.com/someuser',
+ 'https://wechat.com/someuser',
+ 'https://telegram.org/someuser',
+ ];
+
+ blockedUrls.forEach(url => {
+ if (!isUrlBlocked(url)) {
+ console.log(`URL not blocked: ${url}`);
+ }
+ expect(isUrlBlocked(url)).toBe(true);
+ });
+ });
+
+ it('should return false for URLs containing allowed keywords', () => {
+ const allowedUrls = [
+ 'https://www.facebook.com/privacy',
+ 'https://twitter.com/terms',
+ 'https://instagram.com/legal',
+ 'https://www.linkedin.com/help',
+ 'https://pinterest.com/about',
+ 'https://snapchat.com/support',
+ 'https://tiktok.com/contact',
+ 'https://reddit.com/user-agreement',
+ 'https://tumblr.com/policy',
+ 'https://flickr.com/blog',
+ 'https://whatsapp.com/press',
+ 'https://wechat.com/careers',
+ 'https://telegram.org/conditions',
+ 'https://wix.com/careers',
+ ];
+
+ allowedUrls.forEach(url => {
+ expect(isUrlBlocked(url)).toBe(false);
+ });
+ });
+
+ it('should return false for non-blocked URLs', () => {
+ const nonBlockedUrls = [
+ 'https://www.example.com',
+ 'https://www.somewebsite.org',
+ 'https://subdomain.example.com',
+ 'firecrawl.dev',
+ 'amazon.com',
+ 'wix.com',
+ 'https://wix.com'
+ ];
+
+ nonBlockedUrls.forEach(url => {
+ expect(isUrlBlocked(url)).toBe(false);
+ });
+ });
+});
diff --git a/apps/api/src/scraper/WebScraper/utils/blocklist.ts b/apps/api/src/scraper/WebScraper/utils/blocklist.ts
index a50e42e..45d1970 100644
--- a/apps/api/src/scraper/WebScraper/utils/blocklist.ts
+++ b/apps/api/src/scraper/WebScraper/utils/blocklist.ts
@@ -1,5 +1,6 @@
const socialMediaBlocklist = [
'facebook.com',
+ 'x.com',
'twitter.com',
'instagram.com',
'linkedin.com',
@@ -14,14 +15,40 @@ const socialMediaBlocklist = [
'telegram.org',
];
-const allowedUrls = [
- 'linkedin.com/pulse'
+const allowedKeywords = [
+ 'pulse',
+ 'privacy',
+ 'terms',
+ 'policy',
+ 'user-agreement',
+ 'legal',
+ 'help',
+ 'support',
+ 'contact',
+ 'about',
+ 'careers',
+ 'blog',
+ 'press',
+ 'conditions',
];
export function isUrlBlocked(url: string): boolean {
- if (allowedUrls.some(allowedUrl => url.includes(allowedUrl))) {
+ // Check if the URL contains any allowed keywords
+ if (allowedKeywords.some(keyword => url.includes(keyword))) {
return false;
}
- return socialMediaBlocklist.some(domain => url.includes(domain));
+ try {
+ // Check if the URL matches any domain in the blocklist
+ return socialMediaBlocklist.some(domain => {
+ // Create a regular expression to match the exact domain
+ const domainPattern = new RegExp(`(^|\\.)${domain.replace('.', '\\.')}$`);
+ // Test the hostname of the URL against the pattern
+ return domainPattern.test(new URL(url).hostname);
+ });
+ } catch (e) {
+ // If an error occurs (e.g., invalid URL), return false
+ return false;
+ }
}
+
diff --git a/apps/api/src/scraper/WebScraper/utils/custom/website_params.ts b/apps/api/src/scraper/WebScraper/utils/custom/website_params.ts
index 2d9e4e4..af33ced 100644
--- a/apps/api/src/scraper/WebScraper/utils/custom/website_params.ts
+++ b/apps/api/src/scraper/WebScraper/utils/custom/website_params.ts
@@ -171,5 +171,22 @@ export const urlSpecificParams = {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
},
- }
+ },
+ "firecrawl.dev":{
+ defaultScraper: "fire-engine",
+ params: {
+ headers: {
+ "User-Agent":
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-mode": "cors",
+ "sec-fetch-dest": "empty",
+ referer: "https://www.google.com/",
+ "accept-language": "en-US,en;q=0.9",
+ "accept-encoding": "gzip, deflate, br",
+ accept:
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ },
+ },
+ },
};
diff --git a/apps/api/src/scraper/WebScraper/utils/docxProcessor.ts b/apps/api/src/scraper/WebScraper/utils/docxProcessor.ts
index 38759f8..a01b8a2 100644
--- a/apps/api/src/scraper/WebScraper/utils/docxProcessor.ts
+++ b/apps/api/src/scraper/WebScraper/utils/docxProcessor.ts
@@ -5,14 +5,14 @@ import path from "path";
import os from "os";
import mammoth from "mammoth";
-export async function fetchAndProcessDocx(url: string): Promise {
- const tempFilePath = await downloadDocx(url);
+export async function fetchAndProcessDocx(url: string): Promise<{ content: string; pageStatusCode: number; pageError: string }> {
+ const { tempFilePath, pageStatusCode, pageError } = await downloadDocx(url);
const content = await processDocxToText(tempFilePath);
fs.unlinkSync(tempFilePath); // Clean up the temporary file
- return content;
+ return { content, pageStatusCode, pageError };
}
-async function downloadDocx(url: string): Promise {
+async function downloadDocx(url: string): Promise<{ tempFilePath: string; pageStatusCode: number; pageError: string }> {
const response = await axios({
url,
method: "GET",
@@ -25,7 +25,7 @@ async function downloadDocx(url: string): Promise {
response.data.pipe(writer);
return new Promise((resolve, reject) => {
- writer.on("finish", () => resolve(tempFilePath));
+ writer.on("finish", () => resolve({ tempFilePath, pageStatusCode: response.status, pageError: response.statusText != "OK" ? response.statusText : undefined }));
writer.on("error", reject);
});
}
diff --git a/apps/api/src/scraper/WebScraper/utils/metadata.ts b/apps/api/src/scraper/WebScraper/utils/metadata.ts
index ddaf1e8..3f2052c 100644
--- a/apps/api/src/scraper/WebScraper/utils/metadata.ts
+++ b/apps/api/src/scraper/WebScraper/utils/metadata.ts
@@ -29,6 +29,9 @@ interface Metadata {
publishedTime?: string;
articleTag?: string;
articleSection?: string;
+ sourceURL?: string;
+ pageStatusCode?: number;
+ pageError?: string;
}
export function extractMetadata(soup: CheerioAPI, url: string): Metadata {
@@ -61,6 +64,9 @@ export function extractMetadata(soup: CheerioAPI, url: string): Metadata {
let publishedTime: string | null = null;
let articleTag: string | null = null;
let articleSection: string | null = null;
+ let sourceURL: string | null = null;
+ let pageStatusCode: number | null = null;
+ let pageError: string | null = null;
try {
title = soup("title").text() || null;
@@ -132,5 +138,8 @@ export function extractMetadata(soup: CheerioAPI, url: string): Metadata {
...(publishedTime ? { publishedTime } : {}),
...(articleTag ? { articleTag } : {}),
...(articleSection ? { articleSection } : {}),
+ ...(sourceURL ? { sourceURL } : {}),
+ ...(pageStatusCode ? { pageStatusCode } : {}),
+ ...(pageError ? { pageError } : {}),
};
}
diff --git a/apps/api/src/scraper/WebScraper/utils/pdfProcessor.ts b/apps/api/src/scraper/WebScraper/utils/pdfProcessor.ts
index 7c57007..1a67d60 100644
--- a/apps/api/src/scraper/WebScraper/utils/pdfProcessor.ts
+++ b/apps/api/src/scraper/WebScraper/utils/pdfProcessor.ts
@@ -9,14 +9,14 @@ import os from "os";
dotenv.config();
-export async function fetchAndProcessPdf(url: string): Promise {
- const tempFilePath = await downloadPdf(url);
- const content = await processPdfToText(tempFilePath);
+export async function fetchAndProcessPdf(url: string, parsePDF: boolean): Promise<{ content: string, pageStatusCode?: number, pageError?: string }> {
+ const { tempFilePath, pageStatusCode, pageError } = await downloadPdf(url);
+ const content = await processPdfToText(tempFilePath, parsePDF);
fs.unlinkSync(tempFilePath); // Clean up the temporary file
- return content;
+ return { content, pageStatusCode, pageError };
}
-async function downloadPdf(url: string): Promise {
+async function downloadPdf(url: string): Promise<{ tempFilePath: string, pageStatusCode?: number, pageError?: string }> {
const response = await axios({
url,
method: "GET",
@@ -29,15 +29,15 @@ async function downloadPdf(url: string): Promise {
response.data.pipe(writer);
return new Promise((resolve, reject) => {
- writer.on("finish", () => resolve(tempFilePath));
+ writer.on("finish", () => resolve({ tempFilePath, pageStatusCode: response.status, pageError: response.statusText != "OK" ? response.statusText : undefined }));
writer.on("error", reject);
});
}
-export async function processPdfToText(filePath: string): Promise {
+export async function processPdfToText(filePath: string, parsePDF: boolean): Promise {
let content = "";
- if (process.env.LLAMAPARSE_API_KEY) {
+ if (process.env.LLAMAPARSE_API_KEY && parsePDF) {
const apiKey = process.env.LLAMAPARSE_API_KEY;
const headers = {
Authorization: `Bearer ${apiKey}`,
@@ -80,7 +80,7 @@ export async function processPdfToText(filePath: string): Promise {
await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for 0.5 seconds
}
} catch (error) {
- console.error("Error fetching result:", error || '');
+ console.error("Error fetching result w/ LlamaIndex");
attempt++;
await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for 0.5 seconds before retrying
// You may want to handle specific errors differently
@@ -92,11 +92,13 @@ export async function processPdfToText(filePath: string): Promise {
}
content = resultResponse.data[resultType];
} catch (error) {
- console.error("Error processing document:", filePath, error);
+ console.error("Error processing pdf document w/ LlamaIndex(2)");
content = await processPdf(filePath);
}
- } else {
+ } else if (parsePDF) {
content = await processPdf(filePath);
+ } else {
+ content = fs.readFileSync(filePath, "utf-8");
}
return content;
}
diff --git a/apps/api/src/scraper/WebScraper/utils/replacePaths.ts b/apps/api/src/scraper/WebScraper/utils/replacePaths.ts
index d652611..788916c 100644
--- a/apps/api/src/scraper/WebScraper/utils/replacePaths.ts
+++ b/apps/api/src/scraper/WebScraper/utils/replacePaths.ts
@@ -10,7 +10,8 @@ export const replacePathsWithAbsolutePaths = (documents: Document[]): Document[]
) || [];
paths.forEach((path: string) => {
- const isImage = path.startsWith("!");
+ try {
+ const isImage = path.startsWith("!");
let matchedUrl = path.match(/\(([^)]+)\)/) || path.match(/href="([^"]+)"/);
let url = matchedUrl[1];
@@ -22,18 +23,18 @@ export const replacePathsWithAbsolutePaths = (documents: Document[]): Document[]
}
const markdownLinkOrImageText = path.match(/(!?\[.*?\])/)[0];
- if (isImage) {
- document.content = document.content.replace(
- path,
- `${markdownLinkOrImageText}(${url})`
- );
- } else {
+ // Image is handled afterwards
+ if (!isImage) {
document.content = document.content.replace(
path,
`${markdownLinkOrImageText}(${url})`
);
+ }
+ } catch (error) {
+
}
});
+ document.markdown = document.content;
});
return documents;
@@ -60,8 +61,10 @@ export const replaceImgPathsWithAbsolutePaths = (documents: Document[]): Documen
if (!imageUrl.startsWith("http")) {
if (imageUrl.startsWith("/")) {
imageUrl = imageUrl.substring(1);
+ imageUrl = new URL(imageUrl, baseUrl).toString();
+ } else {
+ imageUrl = new URL(imageUrl, document.metadata.sourceURL).toString();
}
- imageUrl = new URL(imageUrl, baseUrl).toString();
}
}
@@ -70,6 +73,7 @@ export const replaceImgPathsWithAbsolutePaths = (documents: Document[]): Documen
`![${altText}](${imageUrl})`
);
});
+ document.markdown = document.content;
});
return documents;
diff --git a/apps/api/src/services/billing/credit_billing.ts b/apps/api/src/services/billing/credit_billing.ts
index 4703a7f..6f06fa1 100644
--- a/apps/api/src/services/billing/credit_billing.ts
+++ b/apps/api/src/services/billing/credit_billing.ts
@@ -1,7 +1,9 @@
+import { NotificationType } from "../../types";
import { withAuth } from "../../lib/withAuth";
+import { sendNotification } from "../notification/email_notification";
import { supabase_service } from "../supabase";
-const FREE_CREDITS = 300;
+const FREE_CREDITS = 500;
export async function billTeam(team_id: string, credits: number) {
return withAuth(supaBillTeam)(team_id, credits);
@@ -34,7 +36,10 @@ export async function supaBillTeam(team_id: string, credits: number) {
let couponCredits = 0;
if (coupons && coupons.length > 0) {
- couponCredits = coupons.reduce((total, coupon) => total + coupon.credits, 0);
+ couponCredits = coupons.reduce(
+ (total, coupon) => total + coupon.credits,
+ 0
+ );
}
let sortedCoupons = coupons.sort((a, b) => b.credits - a.credits);
@@ -53,28 +58,27 @@ export async function supaBillTeam(team_id: string, credits: number) {
usedCredits = usedCredits - sortedCoupons[0].credits;
// update coupon credits
await supabase_service
- .from("coupons")
- .update({
- credits: 0
- })
- .eq("id", sortedCoupons[0].id);
+ .from("coupons")
+ .update({
+ credits: 0,
+ })
+ .eq("id", sortedCoupons[0].id);
sortedCoupons.shift();
-
} else {
// update coupon credits
await supabase_service
- .from("coupons")
- .update({
- credits: sortedCoupons[0].credits - usedCredits
- })
- .eq("id", sortedCoupons[0].id);
+ .from("coupons")
+ .update({
+ credits: sortedCoupons[0].credits - usedCredits,
+ })
+ .eq("id", sortedCoupons[0].id);
usedCredits = 0;
}
}
return await createCreditUsage({ team_id, credits: 0 });
- // not enough coupon credits and no subscription
+ // not enough coupon credits and no subscription
} else {
// update coupon credits
const usedCredits = credits - couponCredits;
@@ -82,7 +86,7 @@ export async function supaBillTeam(team_id: string, credits: number) {
await supabase_service
.from("coupons")
.update({
- credits: 0
+ credits: 0,
})
.eq("id", sortedCoupons[i].id);
}
@@ -90,7 +94,7 @@ export async function supaBillTeam(team_id: string, credits: number) {
return await createCreditUsage({ team_id, credits: usedCredits });
}
}
-
+
// with subscription
// using coupon + subscription credits:
if (credits > couponCredits) {
@@ -99,14 +103,18 @@ export async function supaBillTeam(team_id: string, credits: number) {
await supabase_service
.from("coupons")
.update({
- credits: 0
+ credits: 0,
})
.eq("id", sortedCoupons[i].id);
}
const usedCredits = credits - couponCredits;
- return await createCreditUsage({ team_id, subscription_id: subscription.id, credits: usedCredits });
-
- } else { // using only coupon credits
+ return await createCreditUsage({
+ team_id,
+ subscription_id: subscription.id,
+ credits: usedCredits,
+ });
+ } else {
+ // using only coupon credits
let usedCredits = credits;
while (usedCredits > 0) {
// update coupons
@@ -114,26 +122,29 @@ export async function supaBillTeam(team_id: string, credits: number) {
usedCredits = usedCredits - sortedCoupons[0].credits;
// update coupon credits
await supabase_service
- .from("coupons")
- .update({
- credits: 0
- })
- .eq("id", sortedCoupons[0].id);
+ .from("coupons")
+ .update({
+ credits: 0,
+ })
+ .eq("id", sortedCoupons[0].id);
sortedCoupons.shift();
-
} else {
// update coupon credits
await supabase_service
- .from("coupons")
- .update({
- credits: sortedCoupons[0].credits - usedCredits
- })
- .eq("id", sortedCoupons[0].id);
+ .from("coupons")
+ .update({
+ credits: sortedCoupons[0].credits - usedCredits,
+ })
+ .eq("id", sortedCoupons[0].id);
usedCredits = 0;
}
}
- return await createCreditUsage({ team_id, subscription_id: subscription.id, credits: 0 });
+ return await createCreditUsage({
+ team_id,
+ subscription_id: subscription.id,
+ credits: 0,
+ });
}
}
@@ -142,7 +153,11 @@ export async function supaBillTeam(team_id: string, credits: number) {
return await createCreditUsage({ team_id, credits });
}
- return await createCreditUsage({ team_id, subscription_id: subscription.id, credits });
+ return await createCreditUsage({
+ team_id,
+ subscription_id: subscription.id,
+ credits,
+ });
}
export async function checkTeamCredits(team_id: string, credits: number) {
@@ -155,12 +170,13 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
}
// Retrieve the team's active subscription
- const { data: subscription, error: subscriptionError } = await supabase_service
- .from("subscriptions")
- .select("id, price_id, current_period_start, current_period_end")
- .eq("team_id", team_id)
- .eq("status", "active")
- .single();
+ const { data: subscription, error: subscriptionError } =
+ await supabase_service
+ .from("subscriptions")
+ .select("id, price_id, current_period_start, current_period_end")
+ .eq("team_id", team_id)
+ .eq("status", "active")
+ .single();
// Check for available coupons
const { data: coupons } = await supabase_service
@@ -171,7 +187,10 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
let couponCredits = 0;
if (coupons && coupons.length > 0) {
- couponCredits = coupons.reduce((total, coupon) => total + coupon.credits, 0);
+ couponCredits = coupons.reduce(
+ (total, coupon) => total + coupon.credits,
+ 0
+ );
}
// Free credits, no coupons
@@ -180,19 +199,17 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
if (couponCredits >= credits) {
return { success: true, message: "Sufficient credits available" };
}
-
+
const { data: creditUsages, error: creditUsageError } =
await supabase_service
.from("credit_usage")
.select("credits_used")
.is("subscription_id", null)
.eq("team_id", team_id);
- // .gte("created_at", subscription.current_period_start)
- // .lte("created_at", subscription.current_period_end);
if (creditUsageError) {
throw new Error(
- `Failed to retrieve credit usage for subscription_id: ${subscription.id}`
+ `Failed to retrieve credit usage for team_id: ${team_id}`
);
}
@@ -202,8 +219,32 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
);
console.log("totalCreditsUsed", totalCreditsUsed);
+
+ const end = new Date();
+ end.setDate(end.getDate() + 30);
+ // check if usage is within 80% of the limit
+ const creditLimit = FREE_CREDITS;
+ const creditUsagePercentage = (totalCreditsUsed + credits) / creditLimit;
+
+ if (creditUsagePercentage >= 0.8) {
+ await sendNotification(
+ team_id,
+ NotificationType.APPROACHING_LIMIT,
+ new Date().toISOString(),
+ end.toISOString()
+ );
+ }
+
// 5. Compare the total credits used with the credits allowed by the plan.
if (totalCreditsUsed + credits > FREE_CREDITS) {
+ // Send email notification for insufficient credits
+
+ await sendNotification(
+ team_id,
+ NotificationType.LIMIT_REACHED,
+ new Date().toISOString(),
+ end.toISOString()
+ );
return {
success: false,
message: "Insufficient credits, please upgrade!",
@@ -214,25 +255,24 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
let totalCreditsUsed = 0;
try {
- const { data: creditUsages, error: creditUsageError } = await supabase_service
- .rpc("get_credit_usage_2", {
- sub_id: subscription.id,
- start_time: subscription.current_period_start,
- end_time: subscription.current_period_end
- });
+ const { data: creditUsages, error: creditUsageError } =
+ await supabase_service.rpc("get_credit_usage_2", {
+ sub_id: subscription.id,
+ start_time: subscription.current_period_start,
+ end_time: subscription.current_period_end,
+ });
if (creditUsageError) {
console.error("Error calculating credit usage:", creditUsageError);
}
if (creditUsages && creditUsages.length > 0) {
- totalCreditsUsed = creditUsages[0].total_credits_used;
- // console.log("Total Credits Used:", totalCreditsUsed);
+ totalCreditsUsed = creditUsages[0].total_credits_used;
}
} catch (error) {
console.error("Error calculating credit usage:", error);
-
}
+
// Adjust total credits used by subtracting coupon value
const adjustedCreditsUsed = Math.max(0, totalCreditsUsed - couponCredits);
@@ -244,12 +284,31 @@ export async function supaCheckTeamCredits(team_id: string, credits: number) {
.single();
if (priceError) {
- throw new Error(`Failed to retrieve price for price_id: ${subscription.price_id}`);
+ throw new Error(
+ `Failed to retrieve price for price_id: ${subscription.price_id}`
+ );
}
+ const creditLimit = price.credits;
+ const creditUsagePercentage = (adjustedCreditsUsed + credits) / creditLimit;
+
// Compare the adjusted total credits used with the credits allowed by the plan
if (adjustedCreditsUsed + credits > price.credits) {
+ await sendNotification(
+ team_id,
+ NotificationType.LIMIT_REACHED,
+ subscription.current_period_start,
+ subscription.current_period_end
+ );
return { success: false, message: "Insufficient credits, please upgrade!" };
+ } else if (creditUsagePercentage >= 0.8) {
+ // Send email notification for approaching credit limit
+ await sendNotification(
+ team_id,
+ NotificationType.APPROACHING_LIMIT,
+ subscription.current_period_start,
+ subscription.current_period_end
+ );
}
return { success: true, message: "Sufficient credits available" };
@@ -275,7 +334,10 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
let couponCredits = 0;
if (coupons && coupons.length > 0) {
- couponCredits = coupons.reduce((total, coupon) => total + coupon.credits, 0);
+ couponCredits = coupons.reduce(
+ (total, coupon) => total + coupon.credits,
+ 0
+ );
}
if (subscriptionError || !subscription) {
@@ -288,7 +350,9 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
.eq("team_id", team_id);
if (creditUsageError || !creditUsages) {
- throw new Error(`Failed to retrieve credit usage for team_id: ${team_id}`);
+ throw new Error(
+ `Failed to retrieve credit usage for team_id: ${team_id}`
+ );
}
const totalCreditsUsed = creditUsages.reduce(
@@ -297,7 +361,11 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
);
const remainingCredits = FREE_CREDITS + couponCredits - totalCreditsUsed;
- return { totalCreditsUsed: totalCreditsUsed, remainingCredits, totalCredits: FREE_CREDITS + couponCredits };
+ return {
+ totalCreditsUsed: totalCreditsUsed,
+ remainingCredits,
+ totalCredits: FREE_CREDITS + couponCredits,
+ };
}
const { data: creditUsages, error: creditUsageError } = await supabase_service
@@ -308,10 +376,15 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
.lte("created_at", subscription.current_period_end);
if (creditUsageError || !creditUsages) {
- throw new Error(`Failed to retrieve credit usage for subscription_id: ${subscription.id}`);
+ throw new Error(
+ `Failed to retrieve credit usage for subscription_id: ${subscription.id}`
+ );
}
- const totalCreditsUsed = creditUsages.reduce((acc, usage) => acc + usage.credits_used, 0);
+ const totalCreditsUsed = creditUsages.reduce(
+ (acc, usage) => acc + usage.credits_used,
+ 0
+ );
const { data: price, error: priceError } = await supabase_service
.from("prices")
@@ -320,7 +393,9 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
.single();
if (priceError || !price) {
- throw new Error(`Failed to retrieve price for price_id: ${subscription.price_id}`);
+ throw new Error(
+ `Failed to retrieve price for price_id: ${subscription.price_id}`
+ );
}
const remainingCredits = price.credits + couponCredits - totalCreditsUsed;
@@ -328,11 +403,19 @@ export async function countCreditsAndRemainingForCurrentBillingPeriod(
return {
totalCreditsUsed,
remainingCredits,
- totalCredits: price.credits
+ totalCredits: price.credits,
};
}
-async function createCreditUsage({ team_id, subscription_id, credits }: { team_id: string, subscription_id?: string, credits: number }) {
+async function createCreditUsage({
+ team_id,
+ subscription_id,
+ credits,
+}: {
+ team_id: string;
+ subscription_id?: string;
+ credits: number;
+}) {
const { data: credit_usage } = await supabase_service
.from("credit_usage")
.insert([
@@ -346,4 +429,4 @@ async function createCreditUsage({ team_id, subscription_id, credits }: { team_i
.select();
return { success: true, credit_usage };
-}
\ No newline at end of file
+}
diff --git a/apps/api/src/services/idempotency/create.ts b/apps/api/src/services/idempotency/create.ts
new file mode 100644
index 0000000..ec3e18e
--- /dev/null
+++ b/apps/api/src/services/idempotency/create.ts
@@ -0,0 +1,22 @@
+import { Request } from "express";
+import { supabase_service } from "../supabase";
+
+export async function createIdempotencyKey(
+ req: Request,
+): Promise {
+ const idempotencyKey = req.headers['x-idempotency-key'] as string;
+ if (!idempotencyKey) {
+ throw new Error("No idempotency key provided in the request headers.");
+ }
+
+ const { data, error } = await supabase_service
+ .from("idempotency_keys")
+ .insert({ key: idempotencyKey });
+
+ if (error) {
+ console.error("Failed to create idempotency key:", error);
+ throw error;
+ }
+
+ return idempotencyKey;
+}
diff --git a/apps/api/src/services/idempotency/validate.ts b/apps/api/src/services/idempotency/validate.ts
new file mode 100644
index 0000000..ad6f2c4
--- /dev/null
+++ b/apps/api/src/services/idempotency/validate.ts
@@ -0,0 +1,32 @@
+import { Request } from "express";
+import { supabase_service } from "../supabase";
+import { validate as isUuid } from 'uuid';
+
+export async function validateIdempotencyKey(
+ req: Request,
+): Promise {
+ const idempotencyKey = req.headers['x-idempotency-key'];
+ if (!idempotencyKey) {
+ // // not returning for missing idempotency key for now
+ return true;
+ }
+ if (!isUuid(idempotencyKey)) {
+ console.error("Invalid idempotency key provided in the request headers.");
+ return false;
+ }
+
+ const { data, error } = await supabase_service
+ .from("idempotency_keys")
+ .select("key")
+ .eq("key", idempotencyKey);
+
+ if (error) {
+ console.error(error);
+ }
+
+ if (!data || data.length === 0) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/apps/api/src/services/notification/email_notification.ts b/apps/api/src/services/notification/email_notification.ts
new file mode 100644
index 0000000..e5102ac
--- /dev/null
+++ b/apps/api/src/services/notification/email_notification.ts
@@ -0,0 +1,121 @@
+import { supabase_service } from "../supabase";
+import { withAuth } from "../../lib/withAuth";
+import { Resend } from "resend";
+import { NotificationType } from "../../types";
+
+const emailTemplates: Record<
+ NotificationType,
+ { subject: string; html: string }
+> = {
+ [NotificationType.APPROACHING_LIMIT]: {
+ subject: "You've used 80% of your credit limit - Firecrawl",
+ html: "Hey there,You are approaching your credit limit for this billing period. Your usage right now is around 80% of your total credit limit. Consider upgrading your plan to avoid hitting the limit. Check out our pricing page for more info.
Thanks, Firecrawl Team ",
+ },
+ [NotificationType.LIMIT_REACHED]: {
+ subject:
+ "Credit Limit Reached! Take action now to resume usage - Firecrawl",
+ html: "Hey there,You have reached your credit limit for this billing period. To resume usage, please upgrade your plan. Check out our pricing page for more info.
Thanks, Firecrawl Team ",
+ },
+ [NotificationType.RATE_LIMIT_REACHED]: {
+ subject: "Rate Limit Reached - Firecrawl",
+ html: "Hey there,You've hit one of the Firecrawl endpoint's rate limit! Take a breather and try again in a few moments. If you need higher rate limits, consider upgrading your plan. Check out our pricing page for more info.
If you have any questions, feel free to reach out to us at hello@firecrawl.com
Thanks, Firecrawl Team Ps. this email is only sent once every 7 days if you reach a rate limit.",
+ },
+};
+
+export async function sendNotification(
+ team_id: string,
+ notificationType: NotificationType,
+ startDateString: string,
+ endDateString: string
+) {
+ return withAuth(sendNotificationInternal)(
+ team_id,
+ notificationType,
+ startDateString,
+ endDateString
+ );
+}
+
+async function sendEmailNotification(
+ email: string,
+ notificationType: NotificationType
+) {
+ const resend = new Resend(process.env.RESEND_API_KEY);
+
+ try {
+ const { data, error } = await resend.emails.send({
+ from: "Firecrawl ",
+ to: [email],
+ reply_to: "hello@firecrawl.com",
+ subject: emailTemplates[notificationType].subject,
+ html: emailTemplates[notificationType].html,
+ });
+
+ if (error) {
+ console.error("Error sending email: ", error);
+ return { success: false };
+ }
+ } catch (error) {
+ console.error("Error sending email (2): ", error);
+ return { success: false };
+ }
+}
+
+export async function sendNotificationInternal(
+ team_id: string,
+ notificationType: NotificationType,
+ startDateString: string,
+ endDateString: string
+): Promise<{ success: boolean }> {
+ if (team_id === "preview") {
+ return { success: true };
+ }
+ const { data, error } = await supabase_service
+ .from("user_notifications")
+ .select("*")
+ .eq("team_id", team_id)
+ .eq("notification_type", notificationType)
+ .gte("sent_date", startDateString)
+ .lte("sent_date", endDateString);
+
+ if (error) {
+ console.error("Error fetching notifications: ", error);
+ return { success: false };
+ }
+
+ if (data.length !== 0) {
+ return { success: false };
+ } else {
+ // get the emails from the user with the team_id
+ const { data: emails, error: emailsError } = await supabase_service
+ .from("users")
+ .select("email")
+ .eq("team_id", team_id);
+
+ if (emailsError) {
+ console.error("Error fetching emails: ", emailsError);
+ return { success: false };
+ }
+
+ for (const email of emails) {
+ await sendEmailNotification(email.email, notificationType);
+ }
+
+ const { error: insertError } = await supabase_service
+ .from("user_notifications")
+ .insert([
+ {
+ team_id: team_id,
+ notification_type: notificationType,
+ sent_date: new Date().toISOString(),
+ },
+ ]);
+
+ if (insertError) {
+ console.error("Error inserting notification record: ", insertError);
+ return { success: false };
+ }
+
+ return { success: true };
+ }
+}
diff --git a/apps/api/src/services/queue-service.ts b/apps/api/src/services/queue-service.ts
index 98eb32f..26dabfc 100644
--- a/apps/api/src/services/queue-service.ts
+++ b/apps/api/src/services/queue-service.ts
@@ -1,6 +1,7 @@
import Queue from "bull";
+import { Queue as BullQueue } from "bull";
-let webScraperQueue;
+let webScraperQueue: BullQueue;
export function getWebScraperQueue() {
if (!webScraperQueue) {
diff --git a/apps/api/src/services/queue-worker.ts b/apps/api/src/services/queue-worker.ts
index 6772c57..a42b3e8 100644
--- a/apps/api/src/services/queue-worker.ts
+++ b/apps/api/src/services/queue-worker.ts
@@ -38,7 +38,7 @@ getWebScraperQueue().process(
error: message /* etc... */,
};
- await callWebhook(job.data.team_id, data);
+ await callWebhook(job.data.team_id, job.id as string, data);
await logJob({
success: success,
@@ -78,7 +78,7 @@ getWebScraperQueue().process(
error:
"Something went wrong... Contact help@mendable.ai or try again." /* etc... */,
};
- await callWebhook(job.data.team_id, data);
+ await callWebhook(job.data.team_id, job.id as string, data);
await logJob({
success: false,
message: typeof error === 'string' ? error : (error.message ?? "Something went wrong... Contact help@mendable.ai"),
diff --git a/apps/api/src/services/rate-limiter.ts b/apps/api/src/services/rate-limiter.ts
index e20923c..37b995d 100644
--- a/apps/api/src/services/rate-limiter.ts
+++ b/apps/api/src/services/rate-limiter.ts
@@ -2,47 +2,75 @@ import { RateLimiterRedis } from "rate-limiter-flexible";
import * as redis from "redis";
import { RateLimiterMode } from "../../src/types";
-const MAX_CRAWLS_PER_MINUTE_STARTER = 3;
-const MAX_CRAWLS_PER_MINUTE_STANDARD = 5;
-const MAX_CRAWLS_PER_MINUTE_SCALE = 20;
-
-const MAX_SCRAPES_PER_MINUTE_STARTER = 20;
-const MAX_SCRAPES_PER_MINUTE_STANDARD = 40;
-const MAX_SCRAPES_PER_MINUTE_SCALE = 50;
-
-const MAX_SEARCHES_PER_MINUTE_STARTER = 20;
-const MAX_SEARCHES_PER_MINUTE_STANDARD = 40;
-const MAX_SEARCHES_PER_MINUTE_SCALE = 50;
-
-const MAX_REQUESTS_PER_MINUTE_PREVIEW = 5;
-const MAX_REQUESTS_PER_MINUTE_ACCOUNT = 20;
-const MAX_REQUESTS_PER_MINUTE_CRAWL_STATUS = 150;
+const RATE_LIMITS = {
+ crawl: {
+ default: 3,
+ free: 2,
+ starter: 3,
+ standard: 5,
+ standardOld: 40,
+ scale: 20,
+ hobby: 3,
+ standardNew: 10,
+ growth: 50,
+ },
+ scrape: {
+ default: 20,
+ free: 5,
+ starter: 20,
+ standard: 50,
+ standardOld: 40,
+ scale: 50,
+ hobby: 10,
+ standardNew: 50,
+ growth: 500,
+ },
+ search: {
+ default: 20,
+ free: 5,
+ starter: 20,
+ standard: 40,
+ standardOld: 40,
+ scale: 50,
+ hobby: 10,
+ standardNew: 50,
+ growth: 500,
+ },
+ preview: {
+ free: 5,
+ default: 5,
+ },
+ account: {
+ free: 20,
+ default: 20,
+ },
+ crawlStatus: {
+ free: 150,
+ default: 150,
+ },
+ testSuite: {
+ free: 10000,
+ default: 10000,
+ },
+};
export const redisClient = redis.createClient({
url: process.env.REDIS_URL,
legacyMode: true,
});
-export const previewRateLimiter = new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "preview",
- points: MAX_REQUESTS_PER_MINUTE_PREVIEW,
- duration: 60, // Duration in seconds
-});
+const createRateLimiter = (keyPrefix, points) =>
+ new RateLimiterRedis({
+ storeClient: redisClient,
+ keyPrefix,
+ points,
+ duration: 60, // Duration in seconds
+ });
-export const serverRateLimiter = new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "server",
- points: MAX_REQUESTS_PER_MINUTE_ACCOUNT,
- duration: 60, // Duration in seconds
-});
-
-export const crawlStatusRateLimiter = new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "crawl-status",
- points: MAX_REQUESTS_PER_MINUTE_CRAWL_STATUS,
- duration: 60, // Duration in seconds
-});
+export const serverRateLimiter = createRateLimiter(
+ "server",
+ RATE_LIMITS.account.default
+);
export const testSuiteRateLimiter = new RateLimiterRedis({
storeClient: redisClient,
@@ -51,84 +79,21 @@ export const testSuiteRateLimiter = new RateLimiterRedis({
duration: 60, // Duration in seconds
});
-
-export function getRateLimiter(mode: RateLimiterMode, token: string, plan?: string){
- // Special test suite case. TODO: Change this later.
- if (token.includes("57017") || token.includes("6254cf9")){
+export function getRateLimiter(
+ mode: RateLimiterMode,
+ token: string,
+ plan?: string
+) {
+ if (token.includes("a01ccae") || token.includes("6254cf9")) {
return testSuiteRateLimiter;
}
- switch (mode) {
- case RateLimiterMode.Preview:
- return previewRateLimiter;
- case RateLimiterMode.CrawlStatus:
- return crawlStatusRateLimiter;
- case RateLimiterMode.Crawl:
- if (plan === "standard"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "crawl-standard",
- points: MAX_CRAWLS_PER_MINUTE_STANDARD,
- duration: 60, // Duration in seconds
- });
- } else if (plan === "scale"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "crawl-scale",
- points: MAX_CRAWLS_PER_MINUTE_SCALE,
- duration: 60, // Duration in seconds
- });
- }
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "crawl-starter",
- points: MAX_CRAWLS_PER_MINUTE_STARTER,
- duration: 60, // Duration in seconds
- });
- case RateLimiterMode.Scrape:
- if (plan === "standard"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "scrape-standard",
- points: MAX_SCRAPES_PER_MINUTE_STANDARD,
- duration: 60, // Duration in seconds
- });
- } else if (plan === "scale"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "scrape-scale",
- points: MAX_SCRAPES_PER_MINUTE_SCALE,
- duration: 60, // Duration in seconds
- });
- }
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "scrape-starter",
- points: MAX_SCRAPES_PER_MINUTE_STARTER,
- duration: 60, // Duration in seconds
- });
- case RateLimiterMode.Search:
- if (plan === "standard"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "search-standard",
- points: MAX_SEARCHES_PER_MINUTE_STANDARD,
- duration: 60, // Duration in seconds
- });
- } else if (plan === "scale"){
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "search-scale",
- points: MAX_SEARCHES_PER_MINUTE_SCALE,
- duration: 60, // Duration in seconds
- });
- }
- return new RateLimiterRedis({
- storeClient: redisClient,
- keyPrefix: "search-starter",
- points: MAX_SEARCHES_PER_MINUTE_STARTER,
- duration: 60, // Duration in seconds
- });
- default:
- return serverRateLimiter;
- }
+
+ const rateLimitConfig = RATE_LIMITS[mode]; // {default : 5}
+ if (!rateLimitConfig) return serverRateLimiter;
+
+ const planKey = plan ? plan.replace("-", "") : "default"; // "default"
+ const points =
+ rateLimitConfig[planKey] || rateLimitConfig.default || rateLimitConfig; // 5
+
+ return createRateLimiter(`${mode}-${planKey}`, points);
}
diff --git a/apps/api/src/services/redis.ts b/apps/api/src/services/redis.ts
index f2cedd1..491eeb1 100644
--- a/apps/api/src/services/redis.ts
+++ b/apps/api/src/services/redis.ts
@@ -1,8 +1,35 @@
-import Redis from 'ioredis';
+import Redis from "ioredis";
// Initialize Redis client
const redis = new Redis(process.env.REDIS_URL);
+// Listen to 'error' events to the Redis connection
+redis.on("error", (error) => {
+ try {
+ if (error.message === "ECONNRESET") {
+ console.log("Connection to Redis Session Store timed out.");
+ } else if (error.message === "ECONNREFUSED") {
+ console.log("Connection to Redis Session Store refused!");
+ } else console.log(error);
+ } catch (error) {}
+});
+
+// Listen to 'reconnecting' event to Redis
+redis.on("reconnecting", (err) => {
+ try {
+ if (redis.status === "reconnecting")
+ console.log("Reconnecting to Redis Session Store...");
+ else console.log("Error reconnecting to Redis Session Store.");
+ } catch (error) {}
+});
+
+// Listen to the 'connect' event to Redis
+redis.on("connect", (err) => {
+ try {
+ if (!err) console.log("Connected to Redis Session Store!");
+ } catch (error) {}
+});
+
/**
* Set a value in Redis with an optional expiration time.
* @param {string} key The key under which to store the value.
@@ -11,7 +38,7 @@ const redis = new Redis(process.env.REDIS_URL);
*/
const setValue = async (key: string, value: string, expire?: number) => {
if (expire) {
- await redis.set(key, value, 'EX', expire);
+ await redis.set(key, value, "EX", expire);
} else {
await redis.set(key, value);
}
diff --git a/apps/api/src/services/webhook.ts b/apps/api/src/services/webhook.ts
index ab1f90e..fc5962b 100644
--- a/apps/api/src/services/webhook.ts
+++ b/apps/api/src/services/webhook.ts
@@ -1,46 +1,61 @@
import { supabase_service } from "./supabase";
-export const callWebhook = async (teamId: string, data: any) => {
+export const callWebhook = async (teamId: string, jobId: string,data: any) => {
try {
- const { data: webhooksData, error } = await supabase_service
- .from('webhooks')
- .select('url')
- .eq('team_id', teamId)
- .limit(1);
+ const selfHostedUrl = process.env.SELF_HOSTED_WEBHOOK_URL;
+ const useDbAuthentication = process.env.USE_DB_AUTHENTICATION === 'true';
+ let webhookUrl = selfHostedUrl;
- if (error) {
- console.error(`Error fetching webhook URL for team ID: ${teamId}`, error.message);
- return null;
- }
+ // Only fetch the webhook URL from the database if the self-hosted webhook URL is not set
+ // and the USE_DB_AUTHENTICATION environment variable is set to true
+ if (!selfHostedUrl && useDbAuthentication) {
+ const { data: webhooksData, error } = await supabase_service
+ .from("webhooks")
+ .select("url")
+ .eq("team_id", teamId)
+ .limit(1);
+ if (error) {
+ console.error(
+ `Error fetching webhook URL for team ID: ${teamId}`,
+ error.message
+ );
+ return null;
+ }
- if (!webhooksData || webhooksData.length === 0) {
- return null;
- }
+ if (!webhooksData || webhooksData.length === 0) {
+ return null;
+ }
- let dataToSend = [];
- if (data.result.links && data.result.links.length !== 0) {
- for (let i = 0; i < data.result.links.length; i++) {
- dataToSend.push({
- content: data.result.links[i].content.content,
- markdown: data.result.links[i].content.markdown,
- metadata: data.result.links[i].content.metadata,
- });
+ webhookUrl = webhooksData[0].url;
}
- }
- await fetch(webhooksData[0].url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- success: data.success,
- data: dataToSend,
- error: data.error || undefined,
- }),
+ let dataToSend = [];
+ if (data.result.links && data.result.links.length !== 0) {
+ for (let i = 0; i < data.result.links.length; i++) {
+ dataToSend.push({
+ content: data.result.links[i].content.content,
+ markdown: data.result.links[i].content.markdown,
+ metadata: data.result.links[i].content.metadata,
+ });
+ }
+ }
+
+ await fetch(webhookUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ success: data.success,
+ jobId: jobId,
+ data: dataToSend,
+ error: data.error || undefined,
+ }),
});
} catch (error) {
- console.error(`Error sending webhook for team ID: ${teamId}`, error.message);
+ console.error(
+ `Error sending webhook for team ID: ${teamId}`,
+ error.message
+ );
}
};
-
diff --git a/apps/api/src/types.ts b/apps/api/src/types.ts
index b9b5463..20fb527 100644
--- a/apps/api/src/types.ts
+++ b/apps/api/src/types.ts
@@ -57,6 +57,12 @@ export interface AuthResponse {
team_id?: string;
error?: string;
status?: number;
+ plan?: string;
}
+
-
+export enum NotificationType {
+ APPROACHING_LIMIT = "approachingLimit",
+ LIMIT_REACHED = "limitReached",
+ RATE_LIMIT_REACHED = "rateLimitReached",
+}
\ No newline at end of file
diff --git a/apps/js-sdk/example.js b/apps/js-sdk/example.js
index 5f81192..7f19859 100644
--- a/apps/js-sdk/example.js
+++ b/apps/js-sdk/example.js
@@ -1,3 +1,4 @@
+import { v4 as uuidv4 } from 'uuid';
import FirecrawlApp from '@mendable/firecrawl-js';
import { z } from "zod";
@@ -8,7 +9,8 @@ const scrapeResult = await app.scrapeUrl('firecrawl.dev');
console.log(scrapeResult.data.content)
// Crawl a website:
-const crawlResult = await app.crawlUrl('mendable.ai', {crawlerOptions: {excludes: ['blog/*'], limit: 5}}, false);
+const idempotencyKey = uuidv4(); // optional
+const crawlResult = await app.crawlUrl('mendable.ai', {crawlerOptions: {excludes: ['blog/*'], limit: 5}}, false, 2, idempotencyKey);
console.log(crawlResult)
const jobId = await crawlResult['jobId'];
diff --git a/apps/js-sdk/firecrawl/.env.example b/apps/js-sdk/firecrawl/.env.example
new file mode 100644
index 0000000..6b1780b
--- /dev/null
+++ b/apps/js-sdk/firecrawl/.env.example
@@ -0,0 +1,3 @@
+API_URL=http://localhost:3002
+TEST_API_KEY=fc-YOUR_API_KEY
+
diff --git a/apps/js-sdk/firecrawl/build/index.js b/apps/js-sdk/firecrawl/build/index.js
index b850d5c..b418513 100644
--- a/apps/js-sdk/firecrawl/build/index.js
+++ b/apps/js-sdk/firecrawl/build/index.js
@@ -19,6 +19,7 @@ export default class FirecrawlApp {
* @param {FirecrawlAppConfig} config - Configuration options for the FirecrawlApp instance.
*/
constructor({ apiKey = null }) {
+ this.apiUrl = "https://api.firecrawl.dev";
this.apiKey = apiKey || "";
if (!this.apiKey) {
throw new Error("No API key provided");
@@ -47,7 +48,7 @@ export default class FirecrawlApp {
jsonData = Object.assign(Object.assign({}, jsonData), { extractorOptions: Object.assign(Object.assign({}, params.extractorOptions), { extractionSchema: schema, mode: params.extractorOptions.mode || "llm-extraction" }) });
}
try {
- const response = yield axios.post("https://api.firecrawl.dev/v0/scrape", jsonData, { headers });
+ const response = yield axios.post(this.apiUrl + "/v0/scrape", jsonData, { headers });
if (response.status === 200) {
const responseData = response.data;
if (responseData.success) {
@@ -84,7 +85,7 @@ export default class FirecrawlApp {
jsonData = Object.assign(Object.assign({}, jsonData), params);
}
try {
- const response = yield axios.post("https://api.firecrawl.dev/v0/search", jsonData, { headers });
+ const response = yield axios.post(this.apiUrl + "/v0/search", jsonData, { headers });
if (response.status === 200) {
const responseData = response.data;
if (responseData.success) {
@@ -109,22 +110,23 @@ export default class FirecrawlApp {
* @param {string} url - The URL to crawl.
* @param {Params | null} params - Additional parameters for the crawl request.
* @param {boolean} waitUntilDone - Whether to wait for the crawl job to complete.
- * @param {number} timeout - Timeout in seconds for job status checks.
+ * @param {number} pollInterval - Time in seconds for job status checks.
+ * @param {string} idempotencyKey - Optional idempotency key for the request.
* @returns {Promise} The response from the crawl operation.
*/
crawlUrl(url_1) {
- return __awaiter(this, arguments, void 0, function* (url, params = null, waitUntilDone = true, timeout = 2) {
- const headers = this.prepareHeaders();
+ return __awaiter(this, arguments, void 0, function* (url, params = null, waitUntilDone = true, pollInterval = 2, idempotencyKey) {
+ const headers = this.prepareHeaders(idempotencyKey);
let jsonData = { url };
if (params) {
jsonData = Object.assign(Object.assign({}, jsonData), params);
}
try {
- const response = yield this.postRequest("https://api.firecrawl.dev/v0/crawl", jsonData, headers);
+ const response = yield this.postRequest(this.apiUrl + "/v0/crawl", jsonData, headers);
if (response.status === 200) {
const jobId = response.data.jobId;
if (waitUntilDone) {
- return this.monitorJobStatus(jobId, headers, timeout);
+ return this.monitorJobStatus(jobId, headers, pollInterval);
}
else {
return { success: true, jobId };
@@ -150,9 +152,14 @@ export default class FirecrawlApp {
return __awaiter(this, void 0, void 0, function* () {
const headers = this.prepareHeaders();
try {
- const response = yield this.getRequest(`https://api.firecrawl.dev/v0/crawl/status/${jobId}`, headers);
+ const response = yield this.getRequest(this.apiUrl + `/v0/crawl/status/${jobId}`, headers);
if (response.status === 200) {
- return response.data;
+ return {
+ success: true,
+ status: response.data.status,
+ data: response.data.data,
+ partial_data: !response.data.data ? response.data.partial_data : undefined,
+ };
}
else {
this.handleError(response, "check crawl status");
@@ -172,11 +179,8 @@ export default class FirecrawlApp {
* Prepares the headers for an API request.
* @returns {AxiosRequestHeaders} The prepared headers.
*/
- prepareHeaders() {
- return {
- "Content-Type": "application/json",
- Authorization: `Bearer ${this.apiKey}`,
- };
+ prepareHeaders(idempotencyKey) {
+ return Object.assign({ 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}` }, (idempotencyKey ? { 'x-idempotency-key': idempotencyKey } : {}));
}
/**
* Sends a POST request to the specified URL.
@@ -204,10 +208,10 @@ export default class FirecrawlApp {
* @param {number} timeout - Timeout in seconds for job status checks.
* @returns {Promise} The final job status or data.
*/
- monitorJobStatus(jobId, headers, timeout) {
+ monitorJobStatus(jobId, headers, checkInterval) {
return __awaiter(this, void 0, void 0, function* () {
while (true) {
- const statusResponse = yield this.getRequest(`https://api.firecrawl.dev/v0/crawl/status/${jobId}`, headers);
+ const statusResponse = yield this.getRequest(this.apiUrl + `/v0/crawl/status/${jobId}`, headers);
if (statusResponse.status === 200) {
const statusData = statusResponse.data;
if (statusData.status === "completed") {
@@ -219,10 +223,10 @@ export default class FirecrawlApp {
}
}
else if (["active", "paused", "pending", "queued"].includes(statusData.status)) {
- if (timeout < 2) {
- timeout = 2;
+ if (checkInterval < 2) {
+ checkInterval = 2;
}
- yield new Promise((resolve) => setTimeout(resolve, timeout * 1000)); // Wait for the specified timeout before checking again
+ yield new Promise((resolve) => setTimeout(resolve, checkInterval * 1000)); // Wait for the specified timeout before checking again
}
else {
throw new Error(`Crawl job failed or was stopped. Status: ${statusData.status}`);
diff --git a/apps/js-sdk/firecrawl/package-lock.json b/apps/js-sdk/firecrawl/package-lock.json
index 6b085be..b1cebde 100644
--- a/apps/js-sdk/firecrawl/package-lock.json
+++ b/apps/js-sdk/firecrawl/package-lock.json
@@ -1,22 +1,27 @@
{
"name": "@mendable/firecrawl-js",
- "version": "0.0.17-beta.8",
+ "version": "0.0.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@mendable/firecrawl-js",
- "version": "0.0.17-beta.8",
+ "version": "0.0.22",
"license": "MIT",
"dependencies": {
"axios": "^1.6.8",
+ "dotenv": "^16.4.5",
+ "uuid": "^9.0.1",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.0"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@types/axios": "^0.14.0",
- "@types/node": "^20.12.7",
+ "@types/dotenv": "^8.2.0",
+ "@types/jest": "^29.5.12",
+ "@types/node": "^20.12.12",
+ "@types/uuid": "^9.0.8",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5"
@@ -1013,6 +1018,16 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/dotenv": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.0.tgz",
+ "integrity": "sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==",
+ "deprecated": "This is a stub types definition. dotenv provides its own type definitions, so you do not need this installed.",
+ "dev": true,
+ "dependencies": {
+ "dotenv": "*"
+ }
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -1046,10 +1061,20 @@
"@types/istanbul-lib-report": "*"
}
},
+ "node_modules/@types/jest": {
+ "version": "29.5.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz",
+ "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==",
+ "dev": true,
+ "dependencies": {
+ "expect": "^29.0.0",
+ "pretty-format": "^29.0.0"
+ }
+ },
"node_modules/@types/node": {
- "version": "20.12.7",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
- "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1061,6 +1086,12 @@
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true
},
+ "node_modules/@types/uuid": {
+ "version": "9.0.8",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
+ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
+ "dev": true
+ },
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -1602,6 +1633,17 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.748",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.748.tgz",
@@ -3641,6 +3683,18 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/v8-to-istanbul": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
diff --git a/apps/js-sdk/firecrawl/package.json b/apps/js-sdk/firecrawl/package.json
index 3bacdf4..4ab793c 100644
--- a/apps/js-sdk/firecrawl/package.json
+++ b/apps/js-sdk/firecrawl/package.json
@@ -1,15 +1,15 @@
{
"name": "@mendable/firecrawl-js",
- "version": "0.0.21",
+ "version": "0.0.26",
"description": "JavaScript SDK for Firecrawl API",
"main": "build/index.js",
"types": "types/index.d.ts",
"type": "module",
"scripts": {
"build": "tsc",
- "publish": "npm run build && npm publish --access public",
+ "build-and-publish": "npm run build && npm publish --access public",
"publish-beta": "npm run build && npm publish --access public --tag beta",
- "test": "jest src/**/*.test.ts"
+ "test": "jest src/__tests__/**/*.test.ts"
},
"repository": {
"type": "git",
@@ -19,6 +19,8 @@
"license": "MIT",
"dependencies": {
"axios": "^1.6.8",
+ "dotenv": "^16.4.5",
+ "uuid": "^9.0.1",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.0"
},
@@ -29,7 +31,10 @@
"devDependencies": {
"@jest/globals": "^29.7.0",
"@types/axios": "^0.14.0",
- "@types/node": "^20.12.7",
+ "@types/dotenv": "^8.2.0",
+ "@types/jest": "^29.5.12",
+ "@types/node": "^20.12.12",
+ "@types/uuid": "^9.0.8",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5"
diff --git a/apps/js-sdk/firecrawl/src/__tests__/e2e_withAuth/index.test.ts b/apps/js-sdk/firecrawl/src/__tests__/e2e_withAuth/index.test.ts
new file mode 100644
index 0000000..2725c23
--- /dev/null
+++ b/apps/js-sdk/firecrawl/src/__tests__/e2e_withAuth/index.test.ts
@@ -0,0 +1,155 @@
+import FirecrawlApp from '../../index';
+import { v4 as uuidv4 } from 'uuid';
+import dotenv from 'dotenv';
+
+dotenv.config();
+
+const TEST_API_KEY = process.env.TEST_API_KEY;
+const API_URL = "http://127.0.0.1:3002";
+
+describe('FirecrawlApp E2E Tests', () => {
+ test.concurrent('should throw error for no API key', () => {
+ expect(() => {
+ new FirecrawlApp({ apiKey: null, apiUrl: API_URL });
+ }).toThrow("No API key provided");
+ });
+
+ test.concurrent('should throw error for invalid API key on scrape', async () => {
+ const invalidApp = new FirecrawlApp({ apiKey: "invalid_api_key", apiUrl: API_URL });
+ await expect(invalidApp.scrapeUrl('https://roastmywebsite.ai')).rejects.toThrow("Request failed with status code 401");
+ });
+
+ test.concurrent('should throw error for blocklisted URL on scrape', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const blocklistedUrl = "https://facebook.com/fake-test";
+ await expect(app.scrapeUrl(blocklistedUrl)).rejects.toThrow("Request failed with status code 403");
+ });
+
+ test.concurrent('should return successful response with valid preview token', async () => {
+ const app = new FirecrawlApp({ apiKey: "this_is_just_a_preview_token", apiUrl: API_URL });
+ const response = await app.scrapeUrl('https://roastmywebsite.ai');
+ expect(response).not.toBeNull();
+ expect(response.data.content).toContain("_Roast_");
+ }, 30000); // 30 seconds timeout
+
+ test.concurrent('should return successful response for valid scrape', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.scrapeUrl('https://roastmywebsite.ai');
+ expect(response).not.toBeNull();
+ expect(response.data.content).toContain("_Roast_");
+ expect(response.data).toHaveProperty('markdown');
+ expect(response.data).toHaveProperty('metadata');
+ expect(response.data).not.toHaveProperty('html');
+ }, 30000); // 30 seconds timeout
+
+ test.concurrent('should return successful response with valid API key and include HTML', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.scrapeUrl('https://roastmywebsite.ai', { pageOptions: { includeHtml: true } });
+ expect(response).not.toBeNull();
+ expect(response.data.content).toContain("_Roast_");
+ expect(response.data.markdown).toContain("_Roast_");
+ expect(response.data.html).toContain(" {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.scrapeUrl('https://arxiv.org/pdf/astro-ph/9301001.pdf');
+ expect(response).not.toBeNull();
+ expect(response.data.content).toContain('We present spectrophotometric observations of the Broad Line Radio Galaxy');
+ }, 30000); // 30 seconds timeout
+
+ test.concurrent('should return successful response for valid scrape with PDF file without explicit extension', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.scrapeUrl('https://arxiv.org/pdf/astro-ph/9301001');
+ expect(response).not.toBeNull();
+ expect(response.data.content).toContain('We present spectrophotometric observations of the Broad Line Radio Galaxy');
+ }, 30000); // 30 seconds timeout
+
+ test.concurrent('should throw error for invalid API key on crawl', async () => {
+ const invalidApp = new FirecrawlApp({ apiKey: "invalid_api_key", apiUrl: API_URL });
+ await expect(invalidApp.crawlUrl('https://roastmywebsite.ai')).rejects.toThrow("Request failed with status code 401");
+ });
+
+ test.concurrent('should throw error for blocklisted URL on crawl', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const blocklistedUrl = "https://twitter.com/fake-test";
+ await expect(app.crawlUrl(blocklistedUrl)).rejects.toThrow("Request failed with status code 403");
+ });
+
+ test.concurrent('should return successful response for crawl and wait for completion', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.crawlUrl('https://roastmywebsite.ai', { crawlerOptions: { excludes: ['blog/*'] } }, true, 30);
+ expect(response).not.toBeNull();
+ expect(response[0].content).toContain("_Roast_");
+ }, 60000); // 60 seconds timeout
+
+ test.concurrent('should handle idempotency key for crawl', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const uniqueIdempotencyKey = uuidv4();
+ const response = await app.crawlUrl('https://roastmywebsite.ai', { crawlerOptions: { excludes: ['blog/*'] } }, false, 2, uniqueIdempotencyKey);
+ expect(response).not.toBeNull();
+ expect(response.jobId).toBeDefined();
+
+ await expect(app.crawlUrl('https://roastmywebsite.ai', { crawlerOptions: { excludes: ['blog/*'] } }, true, 2, uniqueIdempotencyKey)).rejects.toThrow("Request failed with status code 409");
+ });
+
+ test.concurrent('should check crawl status', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.crawlUrl('https://roastmywebsite.ai', { crawlerOptions: { excludes: ['blog/*'] } }, false);
+ expect(response).not.toBeNull();
+ expect(response.jobId).toBeDefined();
+
+ let statusResponse = await app.checkCrawlStatus(response.jobId);
+ const maxChecks = 15;
+ let checks = 0;
+
+ while (statusResponse.status === 'active' && checks < maxChecks) {
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ expect(statusResponse.partial_data).not.toBeNull();
+ statusResponse = await app.checkCrawlStatus(response.jobId);
+ checks++;
+ }
+
+ expect(statusResponse).not.toBeNull();
+ expect(statusResponse.status).toBe('completed');
+ expect(statusResponse.data.length).toBeGreaterThan(0);
+ }, 35000); // 35 seconds timeout
+
+ test.concurrent('should return successful response for search', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.search("test query");
+ expect(response).not.toBeNull();
+ expect(response.data[0].content).toBeDefined();
+ expect(response.data.length).toBeGreaterThan(2);
+ }, 30000); // 30 seconds timeout
+
+ test.concurrent('should throw error for invalid API key on search', async () => {
+ const invalidApp = new FirecrawlApp({ apiKey: "invalid_api_key", apiUrl: API_URL });
+ await expect(invalidApp.search("test query")).rejects.toThrow("Request failed with status code 401");
+ });
+
+ test.concurrent('should perform LLM extraction', async () => {
+ const app = new FirecrawlApp({ apiKey: TEST_API_KEY, apiUrl: API_URL });
+ const response = await app.scrapeUrl("https://mendable.ai", {
+ extractorOptions: {
+ mode: 'llm-extraction',
+ extractionPrompt: "Based on the information on the page, find what the company's mission is and whether it supports SSO, and whether it is open source",
+ extractionSchema: {
+ type: 'object',
+ properties: {
+ company_mission: { type: 'string' },
+ supports_sso: { type: 'boolean' },
+ is_open_source: { type: 'boolean' }
+ },
+ required: ['company_mission', 'supports_sso', 'is_open_source']
+ }
+ }
+ });
+ expect(response).not.toBeNull();
+ expect(response.data.llm_extraction).toBeDefined();
+ const llmExtraction = response.data.llm_extraction;
+ expect(llmExtraction.company_mission).toBeDefined();
+ expect(typeof llmExtraction.supports_sso).toBe('boolean');
+ expect(typeof llmExtraction.is_open_source).toBe('boolean');
+ }, 30000); // 30 seconds timeout
+});
diff --git a/apps/js-sdk/firecrawl/src/index.ts b/apps/js-sdk/firecrawl/src/index.ts
index 7654f1b..f884125 100644
--- a/apps/js-sdk/firecrawl/src/index.ts
+++ b/apps/js-sdk/firecrawl/src/index.ts
@@ -6,6 +6,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
*/
export interface FirecrawlAppConfig {
apiKey?: string | null;
+ apiUrl?: string | null;
}
/**
@@ -55,6 +56,7 @@ export interface JobStatusResponse {
status: string;
jobId?: string;
data?: any;
+ partial_data?: any,
error?: string;
}
@@ -63,6 +65,7 @@ export interface JobStatusResponse {
*/
export default class FirecrawlApp {
private apiKey: string;
+ private apiUrl: string = "https://api.firecrawl.dev";
/**
* Initializes a new instance of the FirecrawlApp class.
@@ -107,7 +110,7 @@ export default class FirecrawlApp {
}
try {
const response: AxiosResponse = await axios.post(
- "https://api.firecrawl.dev/v0/scrape",
+ this.apiUrl + "/v0/scrape",
jsonData,
{ headers },
);
@@ -147,7 +150,7 @@ export default class FirecrawlApp {
}
try {
const response: AxiosResponse = await axios.post(
- "https://api.firecrawl.dev/v0/search",
+ this.apiUrl + "/v0/search",
jsonData,
{ headers }
);
@@ -172,30 +175,32 @@ export default class FirecrawlApp {
* @param {string} url - The URL to crawl.
* @param {Params | null} params - Additional parameters for the crawl request.
* @param {boolean} waitUntilDone - Whether to wait for the crawl job to complete.
- * @param {number} timeout - Timeout in seconds for job status checks.
+ * @param {number} pollInterval - Time in seconds for job status checks.
+ * @param {string} idempotencyKey - Optional idempotency key for the request.
* @returns {Promise} The response from the crawl operation.
*/
async crawlUrl(
url: string,
params: Params | null = null,
waitUntilDone: boolean = true,
- timeout: number = 2
+ pollInterval: number = 2,
+ idempotencyKey?: string
): Promise {
- const headers = this.prepareHeaders();
+ const headers = this.prepareHeaders(idempotencyKey);
let jsonData: Params = { url };
if (params) {
jsonData = { ...jsonData, ...params };
}
try {
const response: AxiosResponse = await this.postRequest(
- "https://api.firecrawl.dev/v0/crawl",
+ this.apiUrl + "/v0/crawl",
jsonData,
headers
);
if (response.status === 200) {
const jobId: string = response.data.jobId;
if (waitUntilDone) {
- return this.monitorJobStatus(jobId, headers, timeout);
+ return this.monitorJobStatus(jobId, headers, pollInterval);
} else {
return { success: true, jobId };
}
@@ -218,11 +223,16 @@ export default class FirecrawlApp {
const headers: AxiosRequestHeaders = this.prepareHeaders();
try {
const response: AxiosResponse = await this.getRequest(
- `https://api.firecrawl.dev/v0/crawl/status/${jobId}`,
+ this.apiUrl + `/v0/crawl/status/${jobId}`,
headers
);
if (response.status === 200) {
- return response.data;
+ return {
+ success: true,
+ status: response.data.status,
+ data: response.data.data,
+ partial_data: !response.data.data ? response.data.partial_data : undefined,
+ };
} else {
this.handleError(response, "check crawl status");
}
@@ -240,11 +250,12 @@ export default class FirecrawlApp {
* Prepares the headers for an API request.
* @returns {AxiosRequestHeaders} The prepared headers.
*/
- prepareHeaders(): AxiosRequestHeaders {
+ prepareHeaders(idempotencyKey?: string): AxiosRequestHeaders {
return {
- "Content-Type": "application/json",
- Authorization: `Bearer ${this.apiKey}`,
- } as AxiosRequestHeaders;
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.apiKey}`,
+ ...(idempotencyKey ? { 'x-idempotency-key': idempotencyKey } : {}),
+ } as AxiosRequestHeaders & { 'x-idempotency-key'?: string };
}
/**
@@ -285,11 +296,11 @@ export default class FirecrawlApp {
async monitorJobStatus(
jobId: string,
headers: AxiosRequestHeaders,
- timeout: number
+ checkInterval: number
): Promise {
while (true) {
const statusResponse: AxiosResponse = await this.getRequest(
- `https://api.firecrawl.dev/v0/crawl/status/${jobId}`,
+ this.apiUrl + `/v0/crawl/status/${jobId}`,
headers
);
if (statusResponse.status === 200) {
@@ -303,10 +314,10 @@ export default class FirecrawlApp {
} else if (
["active", "paused", "pending", "queued"].includes(statusData.status)
) {
- if (timeout < 2) {
- timeout = 2;
+ if (checkInterval < 2) {
+ checkInterval = 2;
}
- await new Promise((resolve) => setTimeout(resolve, timeout * 1000)); // Wait for the specified timeout before checking again
+ await new Promise((resolve) => setTimeout(resolve, checkInterval * 1000)); // Wait for the specified timeout before checking again
} else {
throw new Error(
`Crawl job failed or was stopped. Status: ${statusData.status}`
diff --git a/apps/js-sdk/firecrawl/types/index.d.ts b/apps/js-sdk/firecrawl/types/index.d.ts
index 40d95c4..52a7d1e 100644
--- a/apps/js-sdk/firecrawl/types/index.d.ts
+++ b/apps/js-sdk/firecrawl/types/index.d.ts
@@ -5,6 +5,7 @@ import { z } from "zod";
*/
export interface FirecrawlAppConfig {
apiKey?: string | null;
+ apiUrl?: string | null;
}
/**
* Generic parameter interface.
@@ -50,6 +51,7 @@ export interface JobStatusResponse {
status: string;
jobId?: string;
data?: any;
+ partial_data?: any;
error?: string;
}
/**
@@ -57,6 +59,7 @@ export interface JobStatusResponse {
*/
export default class FirecrawlApp {
private apiKey;
+ private apiUrl;
/**
* Initializes a new instance of the FirecrawlApp class.
* @param {FirecrawlAppConfig} config - Configuration options for the FirecrawlApp instance.
@@ -81,10 +84,11 @@ export default class FirecrawlApp {
* @param {string} url - The URL to crawl.
* @param {Params | null} params - Additional parameters for the crawl request.
* @param {boolean} waitUntilDone - Whether to wait for the crawl job to complete.
- * @param {number} timeout - Timeout in seconds for job status checks.
+ * @param {number} pollInterval - Time in seconds for job status checks.
+ * @param {string} idempotencyKey - Optional idempotency key for the request.
* @returns {Promise} The response from the crawl operation.
*/
- crawlUrl(url: string, params?: Params | null, waitUntilDone?: boolean, timeout?: number): Promise;
+ crawlUrl(url: string, params?: Params | null, waitUntilDone?: boolean, pollInterval?: number, idempotencyKey?: string): Promise;
/**
* Checks the status of a crawl job using the Firecrawl API.
* @param {string} jobId - The job ID of the crawl operation.
@@ -95,7 +99,7 @@ export default class FirecrawlApp {
* Prepares the headers for an API request.
* @returns {AxiosRequestHeaders} The prepared headers.
*/
- prepareHeaders(): AxiosRequestHeaders;
+ prepareHeaders(idempotencyKey?: string): AxiosRequestHeaders;
/**
* Sends a POST request to the specified URL.
* @param {string} url - The URL to send the request to.
@@ -118,7 +122,7 @@ export default class FirecrawlApp {
* @param {number} timeout - Timeout in seconds for job status checks.
* @returns {Promise} The final job status or data.
*/
- monitorJobStatus(jobId: string, headers: AxiosRequestHeaders, timeout: number): Promise;
+ monitorJobStatus(jobId: string, headers: AxiosRequestHeaders, checkInterval: number): Promise;
/**
* Handles errors from API responses.
* @param {AxiosResponse} response - The response from the API.
diff --git a/apps/js-sdk/package-lock.json b/apps/js-sdk/package-lock.json
index 4d26319..c59a371 100644
--- a/apps/js-sdk/package-lock.json
+++ b/apps/js-sdk/package-lock.json
@@ -11,8 +11,10 @@
"dependencies": {
"@mendable/firecrawl-js": "^0.0.19",
"axios": "^1.6.8",
+ "dotenv": "^16.4.5",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
+ "uuid": "^9.0.1",
"zod": "^3.23.8"
},
"devDependencies": {
@@ -530,6 +532,17 @@
"node": ">=0.3.1"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/esbuild": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
@@ -743,6 +756,18 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"peer": true
},
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
diff --git a/apps/playwright-service/get_error.py b/apps/playwright-service/get_error.py
new file mode 100644
index 0000000..a33de5e
--- /dev/null
+++ b/apps/playwright-service/get_error.py
@@ -0,0 +1,63 @@
+def get_error(status_code: int) -> str:
+ error_messages = {
+ 300: "Multiple Choices",
+ 301: "Moved Permanently",
+ 302: "Found",
+ 303: "See Other",
+ 304: "Not Modified",
+ 305: "Use Proxy",
+ 307: "Temporary Redirect",
+ 308: "Permanent Redirect",
+ 309: "Resume Incomplete",
+ 310: "Too Many Redirects",
+ 311: "Unavailable For Legal Reasons",
+ 312: "Previously Used",
+ 313: "I'm Used",
+ 314: "Switch Proxy",
+ 315: "Temporary Redirect",
+ 316: "Resume Incomplete",
+ 317: "Too Many Redirects",
+ 400: "Bad Request",
+ 401: "Unauthorized",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 405: "Method Not Allowed",
+ 406: "Not Acceptable",
+ 407: "Proxy Authentication Required",
+ 408: "Request Timeout",
+ 409: "Conflict",
+ 410: "Gone",
+ 411: "Length Required",
+ 412: "Precondition Failed",
+ 413: "Payload Too Large",
+ 414: "URI Too Long",
+ 415: "Unsupported Media Type",
+ 416: "Range Not Satisfiable",
+ 417: "Expectation Failed",
+ 418: "I'm a teapot",
+ 421: "Misdirected Request",
+ 422: "Unprocessable Entity",
+ 423: "Locked",
+ 424: "Failed Dependency",
+ 425: "Too Early",
+ 426: "Upgrade Required",
+ 428: "Precondition Required",
+ 429: "Too Many Requests",
+ 431: "Request Header Fields Too Large",
+ 451: "Unavailable For Legal Reasons",
+ 500: "Internal Server Error",
+ 501: "Not Implemented",
+ 502: "Bad Gateway",
+ 503: "Service Unavailable",
+ 504: "Gateway Timeout",
+ 505: "HTTP Version Not Supported",
+ 506: "Variant Also Negotiates",
+ 507: "Insufficient Storage",
+ 508: "Loop Detected",
+ 510: "Not Extended",
+ 511: "Network Authentication Required",
+ 599: "Network Connect Timeout Error"
+ }
+ if status_code < 300:
+ return None
+ return error_messages.get(status_code, "Unknown Error")
diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py
index c28bc63..bd6b14e 100644
--- a/apps/playwright-service/main.py
+++ b/apps/playwright-service/main.py
@@ -1,38 +1,95 @@
+"""
+This module provides a FastAPI application that uses Playwright to fetch and return
+the HTML content of a specified URL. It supports optional proxy settings and media blocking.
+"""
+
+from os import environ
+
from fastapi import FastAPI
-from playwright.async_api import async_playwright, Browser
from fastapi.responses import JSONResponse
+from playwright.async_api import Browser, async_playwright
from pydantic import BaseModel
+from get_error import get_error
+
+PROXY_SERVER = environ.get("PROXY_SERVER", None)
+PROXY_USERNAME = environ.get("PROXY_USERNAME", None)
+PROXY_PASSWORD = environ.get("PROXY_PASSWORD", None)
+BLOCK_MEDIA = environ.get("BLOCK_MEDIA", "False").upper() == "TRUE"
app = FastAPI()
class UrlModel(BaseModel):
+ """Model representing the URL and associated parameters for the request."""
url: str
- wait: int = None
-
+ wait_after_load: int = 0
+ timeout: int = 15000
+ headers: dict = None
browser: Browser = None
-
@app.on_event("startup")
async def startup_event():
+ """Event handler for application startup to initialize the browser."""
global browser
playwright = await async_playwright().start()
browser = await playwright.chromium.launch()
-
@app.on_event("shutdown")
async def shutdown_event():
+ """Event handler for application shutdown to close the browser."""
await browser.close()
-
@app.post("/html")
async def root(body: UrlModel):
- context = await browser.new_context()
+ """
+ Endpoint to fetch and return HTML content of a given URL.
+
+ Args:
+ body (UrlModel): The URL model containing the target URL, wait time, and timeout.
+
+ Returns:
+ JSONResponse: The HTML content of the page.
+ """
+ context = None
+ if PROXY_SERVER and PROXY_USERNAME and PROXY_PASSWORD:
+ context = await browser.new_context(
+ proxy={
+ "server": PROXY_SERVER,
+ "username": PROXY_USERNAME,
+ "password": PROXY_PASSWORD,
+ }
+ )
+ else:
+ context = await browser.new_context()
+
+ if BLOCK_MEDIA:
+ await context.route(
+ "**/*.{png,jpg,jpeg,gif,svg,mp3,mp4,avi,flac,ogg,wav,webm}",
+ handler=lambda route, request: route.abort(),
+ )
+
page = await context.new_page()
- await page.goto(body.url, timeout=15000) # Set max timeout to 15s
- if body.wait: # Check if wait parameter is provided in the request body
- await page.wait_for_timeout(body.wait) # Convert seconds to milliseconds for playwright
+
+ # Set headers if provided
+ if body.headers:
+ await page.set_extra_http_headers(body.headers)
+
+ response = await page.goto(
+ body.url,
+ wait_until="load",
+ timeout=body.timeout,
+ )
+ page_status_code = response.status
+ page_error = get_error(page_status_code)
+ # Wait != timeout. Wait is the time to wait after the page is loaded - useful in some cases were "load" / "networkidle" is not enough
+ if body.wait_after_load > 0:
+ await page.wait_for_timeout(body.wait_after_load)
+
page_content = await page.content()
await context.close()
- json_compatible_item_data = {"content": page_content}
- return JSONResponse(content=json_compatible_item_data)
+ json_compatible_item_data = {
+ "content": page_content,
+ "pageStatusCode": page_status_code,
+ "pageError": page_error
+ }
+ return JSONResponse(content=json_compatible_item_data)
\ No newline at end of file
diff --git a/apps/python-sdk/.pylintrc b/apps/python-sdk/.pylintrc
new file mode 100644
index 0000000..a580885
--- /dev/null
+++ b/apps/python-sdk/.pylintrc
@@ -0,0 +1,2 @@
+[FORMAT]
+max-line-length = 120
\ No newline at end of file
diff --git a/apps/python-sdk/README.md b/apps/python-sdk/README.md
index 38ca843..ae09973 100644
--- a/apps/python-sdk/README.md
+++ b/apps/python-sdk/README.md
@@ -117,6 +117,25 @@ status = app.check_crawl_status(job_id)
The SDK handles errors returned by the Firecrawl API and raises appropriate exceptions. If an error occurs during a request, an exception will be raised with a descriptive error message.
+## Running the Tests with Pytest
+
+To ensure the functionality of the Firecrawl Python SDK, we have included end-to-end tests using `pytest`. These tests cover various aspects of the SDK, including URL scraping, web searching, and website crawling.
+
+### Running the Tests
+
+To run the tests, execute the following commands:
+
+Install pytest:
+```bash
+pip install pytest
+```
+
+Run:
+```bash
+pytest firecrawl/__tests__/e2e_withAuth/test.py
+```
+
+
## Contributing
Contributions to the Firecrawl Python SDK are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the GitHub repository.
diff --git a/apps/python-sdk/build/lib/firecrawl/firecrawl.py b/apps/python-sdk/build/lib/firecrawl/firecrawl.py
index 98cb8ed..3f50c79 100644
--- a/apps/python-sdk/build/lib/firecrawl/firecrawl.py
+++ b/apps/python-sdk/build/lib/firecrawl/firecrawl.py
@@ -1,25 +1,57 @@
+"""
+FirecrawlApp Module
+
+This module provides a class `FirecrawlApp` for interacting with the Firecrawl API.
+It includes methods to scrape URLs, perform searches, initiate and monitor crawl jobs,
+and check the status of these jobs. The module uses requests for HTTP communication
+and handles retries for certain HTTP status codes.
+
+Classes:
+ - FirecrawlApp: Main class for interacting with the Firecrawl API.
+"""
+
import os
-from typing import Any, Dict, Optional
-import requests
import time
+from typing import Any, Dict, Optional
+
+import requests
+
class FirecrawlApp:
- def __init__(self, api_key=None, api_url='https://api.firecrawl.dev'):
+ """
+ Initialize the FirecrawlApp instance.
+
+ Args:
+ api_key (Optional[str]): API key for authenticating with the Firecrawl API.
+ api_url (Optional[str]): Base URL for the Firecrawl API.
+ """
+ def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None) -> None:
self.api_key = api_key or os.getenv('FIRECRAWL_API_KEY')
if self.api_key is None:
raise ValueError('No API key provided')
- self.api_url = api_url or os.getenv('FIRECRAWL_API_URL')
-
-
-
+ self.api_url = api_url or os.getenv('FIRECRAWL_API_URL', 'https://api.firecrawl.dev')
def scrape_url(self, url: str, params: Optional[Dict[str, Any]] = None) -> Any:
+ """
+ Scrape the specified URL using the Firecrawl API.
+
+ Args:
+ url (str): The URL to scrape.
+ params (Optional[Dict[str, Any]]): Additional parameters for the scrape request.
+
+ Returns:
+ Any: The scraped data if the request is successful.
+
+ Raises:
+ Exception: If the scrape request fails.
+ """
+
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
}
# Prepare the base scrape parameters with the URL
scrape_params = {'url': url}
-
+
# If there are additional params, process them
if params:
# Initialize extractorOptions if present
@@ -32,7 +64,7 @@ class FirecrawlApp:
extractor_options['mode'] = extractor_options.get('mode', 'llm-extraction')
# Update the scrape_params with the processed extractorOptions
scrape_params['extractorOptions'] = extractor_options
-
+
# Include any other params directly at the top level of scrape_params
for key, value in params.items():
if key != 'extractorOptions':
@@ -41,11 +73,11 @@ class FirecrawlApp:
response = requests.post(
f'{self.api_url}/v0/scrape',
headers=headers,
- json=scrape_params
+ json=scrape_params,
)
if response.status_code == 200:
response = response.json()
- if response['success']:
+ if response['success'] and 'data' in response:
return response['data']
else:
raise Exception(f'Failed to scrape URL. Error: {response["error"]}')
@@ -54,8 +86,21 @@ class FirecrawlApp:
raise Exception(f'Failed to scrape URL. Status code: {response.status_code}. Error: {error_message}')
else:
raise Exception(f'Failed to scrape URL. Status code: {response.status_code}')
-
+
def search(self, query, params=None):
+ """
+ Perform a search using the Firecrawl API.
+
+ Args:
+ query (str): The search query.
+ params (Optional[Dict[str, Any]]): Additional parameters for the search request.
+
+ Returns:
+ Any: The search results if the request is successful.
+
+ Raises:
+ Exception: If the search request fails.
+ """
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
@@ -70,19 +115,36 @@ class FirecrawlApp:
)
if response.status_code == 200:
response = response.json()
- if response['success'] == True:
+
+ if response['success'] and 'data' in response:
return response['data']
else:
raise Exception(f'Failed to search. Error: {response["error"]}')
-
+
elif response.status_code in [402, 409, 500]:
error_message = response.json().get('error', 'Unknown error occurred')
raise Exception(f'Failed to search. Status code: {response.status_code}. Error: {error_message}')
else:
raise Exception(f'Failed to search. Status code: {response.status_code}')
- def crawl_url(self, url, params=None, wait_until_done=True, timeout=2):
- headers = self._prepare_headers()
+ def crawl_url(self, url, params=None, wait_until_done=True, timeout=2, idempotency_key=None):
+ """
+ Initiate a crawl job for the specified URL using the Firecrawl API.
+
+ Args:
+ url (str): The URL to crawl.
+ params (Optional[Dict[str, Any]]): Additional parameters for the crawl request.
+ wait_until_done (bool): Whether to wait until the crawl job is completed.
+ timeout (int): Timeout between status checks when waiting for job completion.
+ idempotency_key (Optional[str]): A unique uuid key to ensure idempotency of requests.
+
+ Returns:
+ Any: The crawl job ID or the crawl results if waiting until completion.
+
+ Raises:
+ Exception: If the crawl job initiation or monitoring fails.
+ """
+ headers = self._prepare_headers(idempotency_key)
json_data = {'url': url}
if params:
json_data.update(params)
@@ -97,6 +159,18 @@ class FirecrawlApp:
self._handle_error(response, 'start crawl job')
def check_crawl_status(self, job_id):
+ """
+ Check the status of a crawl job using the Firecrawl API.
+
+ Args:
+ job_id (str): The ID of the crawl job.
+
+ Returns:
+ Any: The status of the crawl job.
+
+ Raises:
+ Exception: If the status check request fails.
+ """
headers = self._prepare_headers()
response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers)
if response.status_code == 200:
@@ -104,13 +178,45 @@ class FirecrawlApp:
else:
self._handle_error(response, 'check crawl status')
- def _prepare_headers(self):
+ def _prepare_headers(self, idempotency_key=None):
+ """
+ Prepare the headers for API requests.
+
+ Args:
+ idempotency_key (Optional[str]): A unique key to ensure idempotency of requests.
+
+ Returns:
+ Dict[str, str]: The headers including content type, authorization, and optionally idempotency key.
+ """
+ if idempotency_key:
+ return {
+ 'Content-Type': 'application/json',
+ 'Authorization': f'Bearer {self.api_key}',
+ 'x-idempotency-key': idempotency_key
+ }
+
return {
'Content-Type': 'application/json',
- 'Authorization': f'Bearer {self.api_key}'
+ 'Authorization': f'Bearer {self.api_key}',
}
def _post_request(self, url, data, headers, retries=3, backoff_factor=0.5):
+ """
+ Make a POST request with retries.
+
+ Args:
+ url (str): The URL to send the POST request to.
+ data (Dict[str, Any]): The JSON data to include in the POST request.
+ headers (Dict[str, str]): The headers to include in the POST request.
+ retries (int): Number of retries for the request.
+ backoff_factor (float): Backoff factor for retries.
+
+ Returns:
+ requests.Response: The response from the POST request.
+
+ Raises:
+ requests.RequestException: If the request fails after the specified retries.
+ """
for attempt in range(retries):
response = requests.post(url, headers=headers, json=data)
if response.status_code == 502:
@@ -120,6 +226,21 @@ class FirecrawlApp:
return response
def _get_request(self, url, headers, retries=3, backoff_factor=0.5):
+ """
+ Make a GET request with retries.
+
+ Args:
+ url (str): The URL to send the GET request to.
+ headers (Dict[str, str]): The headers to include in the GET request.
+ retries (int): Number of retries for the request.
+ backoff_factor (float): Backoff factor for retries.
+
+ Returns:
+ requests.Response: The response from the GET request.
+
+ Raises:
+ requests.RequestException: If the request fails after the specified retries.
+ """
for attempt in range(retries):
response = requests.get(url, headers=headers)
if response.status_code == 502:
@@ -129,7 +250,20 @@ class FirecrawlApp:
return response
def _monitor_job_status(self, job_id, headers, timeout):
- import time
+ """
+ Monitor the status of a crawl job until completion.
+
+ Args:
+ job_id (str): The ID of the crawl job.
+ headers (Dict[str, str]): The headers to include in the status check requests.
+ timeout (int): Timeout between status checks.
+
+ Returns:
+ Any: The crawl results if the job is completed successfully.
+
+ Raises:
+ Exception: If the job fails or an error occurs during status checks.
+ """
while True:
status_response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers)
if status_response.status_code == 200:
@@ -139,9 +273,8 @@ class FirecrawlApp:
return status_data['data']
else:
raise Exception('Crawl job completed but no data was returned')
- elif status_data['status'] in ['active', 'paused', 'pending', 'queued']:
- if timeout < 2:
- timeout = 2
+ elif status_data['status'] in ['active', 'paused', 'pending', 'queued', 'waiting']:
+ timeout=max(timeout,2)
time.sleep(timeout) # Wait for the specified timeout before checking again
else:
raise Exception(f'Crawl job failed or was stopped. Status: {status_data["status"]}')
@@ -149,6 +282,16 @@ class FirecrawlApp:
self._handle_error(status_response, 'check crawl status')
def _handle_error(self, response, action):
+ """
+ Handle errors from API responses.
+
+ Args:
+ response (requests.Response): The response object from the API request.
+ action (str): Description of the action that was being performed.
+
+ Raises:
+ Exception: An exception with a message containing the status code and error details from the response.
+ """
if response.status_code in [402, 408, 409, 500]:
error_message = response.json().get('error', 'Unknown error occurred')
raise Exception(f'Failed to {action}. Status code: {response.status_code}. Error: {error_message}')
diff --git a/apps/python-sdk/dist/firecrawl-py-0.0.12.tar.gz b/apps/python-sdk/dist/firecrawl-py-0.0.12.tar.gz
new file mode 100644
index 0000000..83cd722
Binary files /dev/null and b/apps/python-sdk/dist/firecrawl-py-0.0.12.tar.gz differ
diff --git a/apps/python-sdk/dist/firecrawl-py-0.0.9.tar.gz b/apps/python-sdk/dist/firecrawl-py-0.0.9.tar.gz
deleted file mode 100644
index 55e0eed..0000000
Binary files a/apps/python-sdk/dist/firecrawl-py-0.0.9.tar.gz and /dev/null differ
diff --git a/apps/python-sdk/dist/firecrawl_py-0.0.12-py3-none-any.whl b/apps/python-sdk/dist/firecrawl_py-0.0.12-py3-none-any.whl
new file mode 100644
index 0000000..b96c8f4
Binary files /dev/null and b/apps/python-sdk/dist/firecrawl_py-0.0.12-py3-none-any.whl differ
diff --git a/apps/python-sdk/dist/firecrawl_py-0.0.9-py3-none-any.whl b/apps/python-sdk/dist/firecrawl_py-0.0.9-py3-none-any.whl
deleted file mode 100644
index 83cb5b7..0000000
Binary files a/apps/python-sdk/dist/firecrawl_py-0.0.9-py3-none-any.whl and /dev/null differ
diff --git a/apps/python-sdk/example.py b/apps/python-sdk/example.py
index d83be6d..d80fa79 100644
--- a/apps/python-sdk/example.py
+++ b/apps/python-sdk/example.py
@@ -1,4 +1,5 @@
-from firecrawl import FirecrawlApp
+import uuid
+from firecrawl.firecrawl import FirecrawlApp
app = FirecrawlApp(api_key="fc-YOUR_API_KEY")
@@ -7,7 +8,8 @@ scrape_result = app.scrape_url('firecrawl.dev')
print(scrape_result['markdown'])
# Crawl a website:
-crawl_result = app.crawl_url('mendable.ai', {'crawlerOptions': {'excludes': ['blog/*']}})
+idempotency_key = str(uuid.uuid4()) # optional idempotency key
+crawl_result = app.crawl_url('mendable.ai', {'crawlerOptions': {'excludes': ['blog/*']}}, True, 2, idempotency_key)
print(crawl_result)
# LLM Extraction:
diff --git a/apps/python-sdk/firecrawl/__init__.py b/apps/python-sdk/firecrawl/__init__.py
index e7f8063..fbb2bdb 100644
--- a/apps/python-sdk/firecrawl/__init__.py
+++ b/apps/python-sdk/firecrawl/__init__.py
@@ -1 +1,57 @@
+"""
+This is the Firecrawl package.
+
+This package provides a Python SDK for interacting with the Firecrawl API.
+It includes methods to scrape URLs, perform searches, initiate and monitor crawl jobs,
+and check the status of these jobs.
+
+For more information visit https://github.com/firecrawl/
+"""
+
+import logging
+import os
+
from .firecrawl import FirecrawlApp
+
+__version__ = "0.0.16"
+
+# Define the logger for the Firecrawl project
+logger: logging.Logger = logging.getLogger("firecrawl")
+
+
+def _basic_config() -> None:
+ """Set up basic configuration for logging with a specific format and date format."""
+ try:
+ logging.basicConfig(
+ format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S",
+ )
+ except Exception as e:
+ logger.error("Failed to configure logging: %s", e)
+
+
+def setup_logging() -> None:
+ """Set up logging based on the FIRECRAWL_LOGGING_LEVEL environment variable."""
+ env = os.environ.get(
+ "FIRECRAWL_LOGGING_LEVEL", "INFO"
+ ).upper() # Default to 'INFO' level
+ _basic_config()
+
+ if env == "DEBUG":
+ logger.setLevel(logging.DEBUG)
+ elif env == "INFO":
+ logger.setLevel(logging.INFO)
+ elif env == "WARNING":
+ logger.setLevel(logging.WARNING)
+ elif env == "ERROR":
+ logger.setLevel(logging.ERROR)
+ elif env == "CRITICAL":
+ logger.setLevel(logging.CRITICAL)
+ else:
+ logger.setLevel(logging.INFO)
+ logger.warning("Unknown logging level: %s, defaulting to INFO", env)
+
+
+# Initialize logging configuration when the module is imported
+setup_logging()
+logger.debug("Debugging logger setup")
diff --git a/apps/python-sdk/firecrawl/__pycache__/__init__.cpython-311.pyc b/apps/python-sdk/firecrawl/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index 605b3df..0000000
Binary files a/apps/python-sdk/firecrawl/__pycache__/__init__.cpython-311.pyc and /dev/null differ
diff --git a/apps/python-sdk/firecrawl/__pycache__/firecrawl.cpython-311.pyc b/apps/python-sdk/firecrawl/__pycache__/firecrawl.cpython-311.pyc
deleted file mode 100644
index 7c98fa3..0000000
Binary files a/apps/python-sdk/firecrawl/__pycache__/firecrawl.cpython-311.pyc and /dev/null differ
diff --git a/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/.env.example b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/.env.example
new file mode 100644
index 0000000..904887b
--- /dev/null
+++ b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/.env.example
@@ -0,0 +1,3 @@
+API_URL=http://localhost:3002
+ABSOLUTE_FIRECRAWL_PATH=/Users/user/firecrawl/apps/python-sdk/firecrawl/firecrawl.py
+TEST_API_KEY=fc-YOUR_API_KEY
\ No newline at end of file
diff --git a/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/__init__.py b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/__pycache__/test.cpython-311-pytest-8.2.1.pyc b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/__pycache__/test.cpython-311-pytest-8.2.1.pyc
new file mode 100644
index 0000000..5ba1f13
Binary files /dev/null and b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/__pycache__/test.cpython-311-pytest-8.2.1.pyc differ
diff --git a/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/test.py b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/test.py
new file mode 100644
index 0000000..452d498
--- /dev/null
+++ b/apps/python-sdk/firecrawl/__tests__/e2e_withAuth/test.py
@@ -0,0 +1,168 @@
+import importlib.util
+import pytest
+import time
+import os
+from uuid import uuid4
+from dotenv import load_dotenv
+
+load_dotenv()
+
+API_URL = "http://127.0.0.1:3002";
+ABSOLUTE_FIRECRAWL_PATH = "firecrawl/firecrawl.py"
+TEST_API_KEY = os.getenv('TEST_API_KEY')
+
+print(f"ABSOLUTE_FIRECRAWL_PATH: {ABSOLUTE_FIRECRAWL_PATH}")
+
+spec = importlib.util.spec_from_file_location("FirecrawlApp", ABSOLUTE_FIRECRAWL_PATH)
+firecrawl = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(firecrawl)
+FirecrawlApp = firecrawl.FirecrawlApp
+
+def test_no_api_key():
+ with pytest.raises(Exception) as excinfo:
+ invalid_app = FirecrawlApp(api_url=API_URL)
+ assert "No API key provided" in str(excinfo.value)
+
+def test_scrape_url_invalid_api_key():
+ invalid_app = FirecrawlApp(api_url=API_URL, api_key="invalid_api_key")
+ with pytest.raises(Exception) as excinfo:
+ invalid_app.scrape_url('https://firecrawl.dev')
+ assert "Unexpected error during scrape URL: Status code 401. Unauthorized: Invalid token" in str(excinfo.value)
+
+def test_blocklisted_url():
+ blocklisted_url = "https://facebook.com/fake-test"
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ with pytest.raises(Exception) as excinfo:
+ app.scrape_url(blocklisted_url)
+ assert "Unexpected error during scrape URL: Status code 403. Firecrawl currently does not support social media scraping due to policy restrictions. We're actively working on building support for it." in str(excinfo.value)
+
+def test_successful_response_with_valid_preview_token():
+ app = FirecrawlApp(api_url=API_URL, api_key="this_is_just_a_preview_token")
+ response = app.scrape_url('https://roastmywebsite.ai')
+ assert response is not None
+ assert 'content' in response
+ assert "_Roast_" in response['content']
+
+def test_scrape_url_e2e():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ response = app.scrape_url('https://roastmywebsite.ai')
+ assert response is not None
+ assert 'content' in response
+ assert 'markdown' in response
+ assert 'metadata' in response
+ assert 'html' not in response
+ assert "_Roast_" in response['content']
+
+def test_successful_response_with_valid_api_key_and_include_html():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ response = app.scrape_url('https://roastmywebsite.ai', {'pageOptions': {'includeHtml': True}})
+ assert response is not None
+ assert 'content' in response
+ assert 'markdown' in response
+ assert 'html' in response
+ assert 'metadata' in response
+ assert "_Roast_" in response['content']
+ assert "_Roast_" in response['markdown']
+ assert " 0
+ assert 'content' in response[0]
+ assert "_Roast_" in response[0]['content']
+
+def test_crawl_url_with_idempotency_key_e2e():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ uniqueIdempotencyKey = str(uuid4())
+ response = app.crawl_url('https://roastmywebsite.ai', {'crawlerOptions': {'excludes': ['blog/*']}}, True, 2, uniqueIdempotencyKey)
+ assert response is not None
+ assert len(response) > 0
+ assert 'content' in response[0]
+ assert "_Roast_" in response[0]['content']
+
+ with pytest.raises(Exception) as excinfo:
+ app.crawl_url('https://firecrawl.dev', {'crawlerOptions': {'excludes': ['blog/*']}}, True, 2, uniqueIdempotencyKey)
+ assert "Conflict: Failed to start crawl job due to a conflict. Idempotency key already used" in str(excinfo.value)
+
+def test_check_crawl_status_e2e():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ response = app.crawl_url('https://firecrawl.dev', {'crawlerOptions': {'excludes': ['blog/*']}}, False)
+ assert response is not None
+ assert 'jobId' in response
+
+ time.sleep(30) # wait for 30 seconds
+ status_response = app.check_crawl_status(response['jobId'])
+ assert status_response is not None
+ assert 'status' in status_response
+ assert status_response['status'] == 'completed'
+ assert 'data' in status_response
+ assert len(status_response['data']) > 0
+
+def test_search_e2e():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ response = app.search("test query")
+ assert response is not None
+ assert 'content' in response[0]
+ assert len(response) > 2
+
+def test_search_invalid_api_key():
+ invalid_app = FirecrawlApp(api_url=API_URL, api_key="invalid_api_key")
+ with pytest.raises(Exception) as excinfo:
+ invalid_app.search("test query")
+ assert "Unexpected error during search: Status code 401. Unauthorized: Invalid token" in str(excinfo.value)
+
+def test_llm_extraction():
+ app = FirecrawlApp(api_url=API_URL, api_key=TEST_API_KEY)
+ response = app.scrape_url("https://mendable.ai", {
+ 'extractorOptions': {
+ 'mode': 'llm-extraction',
+ 'extractionPrompt': "Based on the information on the page, find what the company's mission is and whether it supports SSO, and whether it is open source",
+ 'extractionSchema': {
+ 'type': 'object',
+ 'properties': {
+ 'company_mission': {'type': 'string'},
+ 'supports_sso': {'type': 'boolean'},
+ 'is_open_source': {'type': 'boolean'}
+ },
+ 'required': ['company_mission', 'supports_sso', 'is_open_source']
+ }
+ }
+ })
+ assert response is not None
+ assert 'llm_extraction' in response
+ llm_extraction = response['llm_extraction']
+ assert 'company_mission' in llm_extraction
+ assert isinstance(llm_extraction['supports_sso'], bool)
+ assert isinstance(llm_extraction['is_open_source'], bool)
\ No newline at end of file
diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py
index 98cb8ed..7ec0d33 100644
--- a/apps/python-sdk/firecrawl/firecrawl.py
+++ b/apps/python-sdk/firecrawl/firecrawl.py
@@ -1,25 +1,63 @@
+"""
+FirecrawlApp Module
+
+This module provides a class `FirecrawlApp` for interacting with the Firecrawl API.
+It includes methods to scrape URLs, perform searches, initiate and monitor crawl jobs,
+and check the status of these jobs. The module uses requests for HTTP communication
+and handles retries for certain HTTP status codes.
+
+Classes:
+ - FirecrawlApp: Main class for interacting with the Firecrawl API.
+"""
+import logging
import os
-from typing import Any, Dict, Optional
-import requests
import time
+from typing import Any, Dict, Optional
+
+import requests
+
+logger : logging.Logger = logging.getLogger("firecrawl")
class FirecrawlApp:
- def __init__(self, api_key=None, api_url='https://api.firecrawl.dev'):
+ """
+ Initialize the FirecrawlApp instance.
+
+ Args:
+ api_key (Optional[str]): API key for authenticating with the Firecrawl API.
+ api_url (Optional[str]): Base URL for the Firecrawl API.
+ """
+ def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None) -> None:
self.api_key = api_key or os.getenv('FIRECRAWL_API_KEY')
if self.api_key is None:
+ logger.warning("No API key provided")
raise ValueError('No API key provided')
- self.api_url = api_url or os.getenv('FIRECRAWL_API_URL')
-
-
+ else:
+ logger.debug("Initialized FirecrawlApp with API key: %s", self.api_key)
+
+ self.api_url = api_url or os.getenv('FIRECRAWL_API_URL', 'https://api.firecrawl.dev')
+ if self.api_url != 'https://api.firecrawl.dev':
+ logger.debug("Initialized FirecrawlApp with API URL: %s", self.api_url)
def scrape_url(self, url: str, params: Optional[Dict[str, Any]] = None) -> Any:
- headers = {
- 'Content-Type': 'application/json',
- 'Authorization': f'Bearer {self.api_key}'
- }
+ """
+ Scrape the specified URL using the Firecrawl API.
+
+ Args:
+ url (str): The URL to scrape.
+ params (Optional[Dict[str, Any]]): Additional parameters for the scrape request.
+
+ Returns:
+ Any: The scraped data if the request is successful.
+
+ Raises:
+ Exception: If the scrape request fails.
+ """
+
+ headers = self._prepare_headers()
+
# Prepare the base scrape parameters with the URL
scrape_params = {'url': url}
-
+
# If there are additional params, process them
if params:
# Initialize extractorOptions if present
@@ -32,7 +70,7 @@ class FirecrawlApp:
extractor_options['mode'] = extractor_options.get('mode', 'llm-extraction')
# Update the scrape_params with the processed extractorOptions
scrape_params['extractorOptions'] = extractor_options
-
+
# Include any other params directly at the top level of scrape_params
for key, value in params.items():
if key != 'extractorOptions':
@@ -41,25 +79,32 @@ class FirecrawlApp:
response = requests.post(
f'{self.api_url}/v0/scrape',
headers=headers,
- json=scrape_params
+ json=scrape_params,
)
if response.status_code == 200:
response = response.json()
- if response['success']:
+ if response['success'] and 'data' in response:
return response['data']
else:
raise Exception(f'Failed to scrape URL. Error: {response["error"]}')
- elif response.status_code in [402, 408, 409, 500]:
- error_message = response.json().get('error', 'Unknown error occurred')
- raise Exception(f'Failed to scrape URL. Status code: {response.status_code}. Error: {error_message}')
else:
- raise Exception(f'Failed to scrape URL. Status code: {response.status_code}')
-
- def search(self, query, params=None):
- headers = {
- 'Content-Type': 'application/json',
- 'Authorization': f'Bearer {self.api_key}'
- }
+ self._handle_error(response, 'scrape URL')
+
+ def search(self, query: str, params: Optional[Dict[str, Any]] = None) -> Any:
+ """
+ Perform a search using the Firecrawl API.
+
+ Args:
+ query (str): The search query.
+ params (Optional[Dict[str, Any]]): Additional parameters for the search request.
+
+ Returns:
+ Any: The search results if the request is successful.
+
+ Raises:
+ Exception: If the search request fails.
+ """
+ headers = self._prepare_headers()
json_data = {'query': query}
if params:
json_data.update(params)
@@ -70,19 +115,37 @@ class FirecrawlApp:
)
if response.status_code == 200:
response = response.json()
- if response['success'] == True:
+
+ if response['success'] and 'data' in response:
return response['data']
else:
raise Exception(f'Failed to search. Error: {response["error"]}')
-
- elif response.status_code in [402, 409, 500]:
- error_message = response.json().get('error', 'Unknown error occurred')
- raise Exception(f'Failed to search. Status code: {response.status_code}. Error: {error_message}')
- else:
- raise Exception(f'Failed to search. Status code: {response.status_code}')
- def crawl_url(self, url, params=None, wait_until_done=True, timeout=2):
- headers = self._prepare_headers()
+ else:
+ self._handle_error(response, 'search')
+
+ def crawl_url(self, url: str,
+ params: Optional[Dict[str, Any]] = None,
+ wait_until_done: bool = True,
+ poll_interval: int = 2,
+ idempotency_key: Optional[str] = None) -> Any:
+ """
+ Initiate a crawl job for the specified URL using the Firecrawl API.
+
+ Args:
+ url (str): The URL to crawl.
+ params (Optional[Dict[str, Any]]): Additional parameters for the crawl request.
+ wait_until_done (bool): Whether to wait until the crawl job is completed.
+ poll_interval (int): Time in seconds between status checks when waiting for job completion.
+ idempotency_key (Optional[str]): A unique uuid key to ensure idempotency of requests.
+
+ Returns:
+ Any: The crawl job ID or the crawl results if waiting until completion.
+
+ Raises:
+ Exception: If the crawl job initiation or monitoring fails.
+ """
+ headers = self._prepare_headers(idempotency_key)
json_data = {'url': url}
if params:
json_data.update(params)
@@ -90,13 +153,25 @@ class FirecrawlApp:
if response.status_code == 200:
job_id = response.json().get('jobId')
if wait_until_done:
- return self._monitor_job_status(job_id, headers, timeout)
+ return self._monitor_job_status(job_id, headers, poll_interval)
else:
return {'jobId': job_id}
else:
self._handle_error(response, 'start crawl job')
- def check_crawl_status(self, job_id):
+ def check_crawl_status(self, job_id: str) -> Any:
+ """
+ Check the status of a crawl job using the Firecrawl API.
+
+ Args:
+ job_id (str): The ID of the crawl job.
+
+ Returns:
+ Any: The status of the crawl job.
+
+ Raises:
+ Exception: If the status check request fails.
+ """
headers = self._prepare_headers()
response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers)
if response.status_code == 200:
@@ -104,13 +179,49 @@ class FirecrawlApp:
else:
self._handle_error(response, 'check crawl status')
- def _prepare_headers(self):
+ def _prepare_headers(self, idempotency_key: Optional[str] = None) -> Dict[str, str]:
+ """
+ Prepare the headers for API requests.
+
+ Args:
+ idempotency_key (Optional[str]): A unique key to ensure idempotency of requests.
+
+ Returns:
+ Dict[str, str]: The headers including content type, authorization, and optionally idempotency key.
+ """
+ if idempotency_key:
+ return {
+ 'Content-Type': 'application/json',
+ 'Authorization': f'Bearer {self.api_key}',
+ 'x-idempotency-key': idempotency_key
+ }
+
return {
'Content-Type': 'application/json',
- 'Authorization': f'Bearer {self.api_key}'
+ 'Authorization': f'Bearer {self.api_key}',
}
- def _post_request(self, url, data, headers, retries=3, backoff_factor=0.5):
+ def _post_request(self, url: str,
+ data: Dict[str, Any],
+ headers: Dict[str, str],
+ retries: int = 3,
+ backoff_factor: float = 0.5) -> requests.Response:
+ """
+ Make a POST request with retries.
+
+ Args:
+ url (str): The URL to send the POST request to.
+ data (Dict[str, Any]): The JSON data to include in the POST request.
+ headers (Dict[str, str]): The headers to include in the POST request.
+ retries (int): Number of retries for the request.
+ backoff_factor (float): Backoff factor for retries.
+
+ Returns:
+ requests.Response: The response from the POST request.
+
+ Raises:
+ requests.RequestException: If the request fails after the specified retries.
+ """
for attempt in range(retries):
response = requests.post(url, headers=headers, json=data)
if response.status_code == 502:
@@ -119,7 +230,25 @@ class FirecrawlApp:
return response
return response
- def _get_request(self, url, headers, retries=3, backoff_factor=0.5):
+ def _get_request(self, url: str,
+ headers: Dict[str, str],
+ retries: int = 3,
+ backoff_factor: float = 0.5) -> requests.Response:
+ """
+ Make a GET request with retries.
+
+ Args:
+ url (str): The URL to send the GET request to.
+ headers (Dict[str, str]): The headers to include in the GET request.
+ retries (int): Number of retries for the request.
+ backoff_factor (float): Backoff factor for retries.
+
+ Returns:
+ requests.Response: The response from the GET request.
+
+ Raises:
+ requests.RequestException: If the request fails after the specified retries.
+ """
for attempt in range(retries):
response = requests.get(url, headers=headers)
if response.status_code == 502:
@@ -128,8 +257,21 @@ class FirecrawlApp:
return response
return response
- def _monitor_job_status(self, job_id, headers, timeout):
- import time
+ def _monitor_job_status(self, job_id: str, headers: Dict[str, str], poll_interval: int) -> Any:
+ """
+ Monitor the status of a crawl job until completion.
+
+ Args:
+ job_id (str): The ID of the crawl job.
+ headers (Dict[str, str]): The headers to include in the status check requests.
+ poll_interval (int): Secounds between status checks.
+
+ Returns:
+ Any: The crawl results if the job is completed successfully.
+
+ Raises:
+ Exception: If the job fails or an error occurs during status checks.
+ """
while True:
status_response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers)
if status_response.status_code == 200:
@@ -139,18 +281,38 @@ class FirecrawlApp:
return status_data['data']
else:
raise Exception('Crawl job completed but no data was returned')
- elif status_data['status'] in ['active', 'paused', 'pending', 'queued']:
- if timeout < 2:
- timeout = 2
- time.sleep(timeout) # Wait for the specified timeout before checking again
+ elif status_data['status'] in ['active', 'paused', 'pending', 'queued', 'waiting']:
+ poll_interval=max(poll_interval,2)
+ time.sleep(poll_interval) # Wait for the specified interval before checking again
else:
raise Exception(f'Crawl job failed or was stopped. Status: {status_data["status"]}')
else:
self._handle_error(status_response, 'check crawl status')
- def _handle_error(self, response, action):
- if response.status_code in [402, 408, 409, 500]:
- error_message = response.json().get('error', 'Unknown error occurred')
- raise Exception(f'Failed to {action}. Status code: {response.status_code}. Error: {error_message}')
+ def _handle_error(self, response: requests.Response, action: str) -> None:
+ """
+ Handle errors from API responses.
+
+ Args:
+ response (requests.Response): The response object from the API request.
+ action (str): Description of the action that was being performed.
+
+ Raises:
+ Exception: An exception with a message containing the status code and error details from the response.
+ """
+ error_message = response.json().get('error', 'No additional error details provided.')
+
+ if response.status_code == 402:
+ message = f"Payment Required: Failed to {action}. {error_message}"
+ elif response.status_code == 408:
+ message = f"Request Timeout: Failed to {action} as the request timed out. {error_message}"
+ elif response.status_code == 409:
+ message = f"Conflict: Failed to {action} due to a conflict. {error_message}"
+ elif response.status_code == 500:
+ message = f"Internal Server Error: Failed to {action}. {error_message}"
else:
- raise Exception(f'Unexpected error occurred while trying to {action}. Status code: {response.status_code}')
+ message = f"Unexpected error during {action}: Status code {response.status_code}. {error_message}"
+
+ # Raise an HTTPError with the custom message and attach the response
+ raise requests.exceptions.HTTPError(message, response=response)
+
\ No newline at end of file
diff --git a/apps/python-sdk/firecrawl_py.egg-info/PKG-INFO b/apps/python-sdk/firecrawl_py.egg-info/PKG-INFO
index c1ee531..288eb7a 100644
--- a/apps/python-sdk/firecrawl_py.egg-info/PKG-INFO
+++ b/apps/python-sdk/firecrawl_py.egg-info/PKG-INFO
@@ -1,7 +1,179 @@
Metadata-Version: 2.1
Name: firecrawl-py
-Version: 0.0.9
+Version: 0.0.12
Summary: Python SDK for Firecrawl API
Home-page: https://github.com/mendableai/firecrawl
Author: Mendable.ai
Author-email: nick@mendable.ai
+License: GNU General Public License v3 (GPLv3)
+Project-URL: Documentation, https://docs.firecrawl.dev
+Project-URL: Source, https://github.com/mendableai/firecrawl
+Project-URL: Tracker, https://github.com/mendableai/firecrawl/issues
+Keywords: SDK API firecrawl
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Topic :: Internet
+Classifier: Topic :: Internet :: WWW/HTTP
+Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
+Classifier: Topic :: Software Development
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing
+Classifier: Topic :: Text Processing :: Indexing
+Requires-Python: >=3.8
+Description-Content-Type: text/markdown
+
+# Firecrawl Python SDK
+
+The Firecrawl Python SDK is a library that allows you to easily scrape and crawl websites, and output the data in a format ready for use with language models (LLMs). It provides a simple and intuitive interface for interacting with the Firecrawl API.
+
+## Installation
+
+To install the Firecrawl Python SDK, you can use pip:
+
+```bash
+pip install firecrawl-py
+```
+
+## Usage
+
+1. Get an API key from [firecrawl.dev](https://firecrawl.dev)
+2. Set the API key as an environment variable named `FIRECRAWL_API_KEY` or pass it as a parameter to the `FirecrawlApp` class.
+
+
+Here's an example of how to use the SDK:
+
+```python
+from firecrawl import FirecrawlApp
+
+# Initialize the FirecrawlApp with your API key
+app = FirecrawlApp(api_key='your_api_key')
+
+# Scrape a single URL
+url = 'https://mendable.ai'
+scraped_data = app.scrape_url(url)
+
+# Crawl a website
+crawl_url = 'https://mendable.ai'
+params = {
+ 'pageOptions': {
+ 'onlyMainContent': True
+ }
+}
+crawl_result = app.crawl_url(crawl_url, params=params)
+```
+
+### Scraping a URL
+
+To scrape a single URL, use the `scrape_url` method. It takes the URL as a parameter and returns the scraped data as a dictionary.
+
+```python
+url = 'https://example.com'
+scraped_data = app.scrape_url(url)
+```
+### Extracting structured data from a URL
+
+With LLM extraction, you can easily extract structured data from any URL. We support pydantic schemas to make it easier for you too. Here is how you to use it:
+
+```python
+class ArticleSchema(BaseModel):
+ title: str
+ points: int
+ by: str
+ commentsURL: str
+
+class TopArticlesSchema(BaseModel):
+ top: List[ArticleSchema] = Field(..., max_items=5, description="Top 5 stories")
+
+data = app.scrape_url('https://news.ycombinator.com', {
+ 'extractorOptions': {
+ 'extractionSchema': TopArticlesSchema.model_json_schema(),
+ 'mode': 'llm-extraction'
+ },
+ 'pageOptions':{
+ 'onlyMainContent': True
+ }
+})
+print(data["llm_extraction"])
+```
+
+### Search for a query
+
+Used to search the web, get the most relevant results, scrap each page and return the markdown.
+
+```python
+query = 'what is mendable?'
+search_result = app.search(query)
+```
+
+### Crawling a Website
+
+To crawl a website, use the `crawl_url` method. It takes the starting URL and optional parameters as arguments. The `params` argument allows you to specify additional options for the crawl job, such as the maximum number of pages to crawl, allowed domains, and the output format.
+
+The `wait_until_done` parameter determines whether the method should wait for the crawl job to complete before returning the result. If set to `True`, the method will periodically check the status of the crawl job until it is completed or the specified `timeout` (in seconds) is reached. If set to `False`, the method will return immediately with the job ID, and you can manually check the status of the crawl job using the `check_crawl_status` method.
+
+```python
+crawl_url = 'https://example.com'
+params = {
+ 'crawlerOptions': {
+ 'excludes': ['blog/*'],
+ 'includes': [], # leave empty for all pages
+ 'limit': 1000,
+ },
+ 'pageOptions': {
+ 'onlyMainContent': True
+ }
+}
+crawl_result = app.crawl_url(crawl_url, params=params, wait_until_done=True, timeout=5)
+```
+
+If `wait_until_done` is set to `True`, the `crawl_url` method will return the crawl result once the job is completed. If the job fails or is stopped, an exception will be raised.
+
+### Checking Crawl Status
+
+To check the status of a crawl job, use the `check_crawl_status` method. It takes the job ID as a parameter and returns the current status of the crawl job.
+
+```python
+job_id = crawl_result['jobId']
+status = app.check_crawl_status(job_id)
+```
+
+## Error Handling
+
+The SDK handles errors returned by the Firecrawl API and raises appropriate exceptions. If an error occurs during a request, an exception will be raised with a descriptive error message.
+
+## Running the Tests with Pytest
+
+To ensure the functionality of the Firecrawl Python SDK, we have included end-to-end tests using `pytest`. These tests cover various aspects of the SDK, including URL scraping, web searching, and website crawling.
+
+### Running the Tests
+
+To run the tests, execute the following commands:
+
+Install pytest:
+```bash
+pip install pytest
+```
+
+Run:
+```bash
+pytest firecrawl/__tests__/e2e_withAuth/test.py
+```
+
+
+## Contributing
+
+Contributions to the Firecrawl Python SDK are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the GitHub repository.
+
+## License
+
+The Firecrawl Python SDK is open-source and released under the [MIT License](https://opensource.org/licenses/MIT).
diff --git a/apps/python-sdk/firecrawl_py.egg-info/requires.txt b/apps/python-sdk/firecrawl_py.egg-info/requires.txt
index f229360..c8d341f 100644
--- a/apps/python-sdk/firecrawl_py.egg-info/requires.txt
+++ b/apps/python-sdk/firecrawl_py.egg-info/requires.txt
@@ -1 +1,3 @@
requests
+pytest
+python-dotenv
diff --git a/apps/python-sdk/pyproject.toml b/apps/python-sdk/pyproject.toml
new file mode 100644
index 0000000..25fa7c1
--- /dev/null
+++ b/apps/python-sdk/pyproject.toml
@@ -0,0 +1,48 @@
+[build-system]
+requires = ["setuptools>=42", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+dynamic = ["version"]
+name = "firecrawl-py"
+description = "Python SDK for Firecrawl API"
+readme = {file="README.md", content-type = "text/markdown"}
+requires-python = ">=3.8"
+dependencies = [
+ "requests",
+]
+authors = [{name = "Mendable.ai",email = "nick@mendable.ai"}]
+maintainers = [{name = "Mendable.ai",email = "nick@mendable.ai"}]
+license = {text = "GNU General Public License v3 (GPLv3)"}
+
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Topic :: Internet",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
+ "Topic :: Software Development",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Text Processing",
+ "Topic :: Text Processing :: Indexing",
+]
+
+keywords = ["SDK", "API", "firecrawl"]
+
+[project.urls]
+"Documentation" = "https://docs.firecrawl.dev"
+"Source" = "https://github.com/mendableai/firecrawl"
+"Tracker" = "https://github.com/mendableai/firecrawl/issues"
+
+[tool.setuptools.packages.find]
+where = ["."]
\ No newline at end of file
diff --git a/apps/python-sdk/requirements.txt b/apps/python-sdk/requirements.txt
new file mode 100644
index 0000000..1bed588
--- /dev/null
+++ b/apps/python-sdk/requirements.txt
@@ -0,0 +1,3 @@
+requests
+pytest
+python-dotenv
\ No newline at end of file
diff --git a/apps/python-sdk/setup.py b/apps/python-sdk/setup.py
index 7df520e..4978559 100644
--- a/apps/python-sdk/setup.py
+++ b/apps/python-sdk/setup.py
@@ -1,14 +1,63 @@
-from setuptools import setup, find_packages
+import re
+from pathlib import Path
+
+from setuptools import find_packages, setup
+
+this_directory = Path(__file__).parent
+long_description_content = (this_directory / "README.md").read_text()
+
+
+def get_version():
+ """Dynamically set version"""
+ version_file = (this_directory / "firecrawl" / "__init__.py").read_text()
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
+ if version_match:
+ return version_match.group(1)
+ raise RuntimeError("Unable to find version string.")
+
setup(
- name='firecrawl-py',
- version='0.0.9',
- url='https://github.com/mendableai/firecrawl',
- author='Mendable.ai',
- author_email='nick@mendable.ai',
- description='Python SDK for Firecrawl API',
+ name="firecrawl-py",
+ version=get_version(),
+ url="https://github.com/mendableai/firecrawl",
+ author="Mendable.ai",
+ author_email="nick@mendable.ai",
+ description="Python SDK for Firecrawl API",
+ long_description=long_description_content,
+ long_description_content_type="text/markdown",
packages=find_packages(),
install_requires=[
'requests',
+ 'pytest',
+ 'python-dotenv',
],
+ python_requires=">=3.8",
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Topic :: Internet",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
+ "Topic :: Software Development",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Text Processing",
+ "Topic :: Text Processing :: Indexing",
+ ],
+ keywords="SDK API firecrawl",
+ project_urls={
+ "Documentation": "https://docs.firecrawl.dev",
+ "Source": "https://github.com/mendableai/firecrawl",
+ "Tracker": "https://github.com/mendableai/firecrawl/issues",
+ },
+ license="GNU General Public License v3 (GPLv3)",
)
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 049672d..c95ccc9 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -5,6 +5,10 @@ services:
build: apps/playwright-service
environment:
- PORT=3000
+ - PROXY_SERVER=${PROXY_SERVER}
+ - PROXY_USERNAME=${PROXY_USERNAME}
+ - PROXY_PASSWORD=${PROXY_PASSWORD}
+ - BLOCK_MEDIA=${BLOCK_MEDIA}
networks:
- backend
diff --git a/examples/kubernetes-cluster-install/README.md b/examples/kubernetes-cluster-install/README.md
new file mode 100644
index 0000000..f874d82
--- /dev/null
+++ b/examples/kubernetes-cluster-install/README.md
@@ -0,0 +1,41 @@
+# Install Firecrawl on a Kubernetes Cluster (Simple Version)
+# Before installing
+1. Set [secret.yaml](secret.yaml) and [configmap.yaml](configmap.yaml) and do not check in secrets
+2. Build Docker images, and host it in your Docker Registry (replace the target registry with your own)
+ 1. API (which is also used as a worker image)
+ 1. ```bash
+ docker build -t ghcr.io/winkk-dev/firecrawl:latest ../../apps/api
+ docker push ghcr.io/winkk-dev/firecrawl:latest
+ ```
+ 2. Playwright
+ 1. ```bash
+ docker build -t ghcr.io/winkk-dev/firecrawl-playwright:latest ../../apps/playwright-service
+ docker push ghcr.io/winkk-dev/firecrawl-playwright:latest
+ ```
+3. Replace the image in [worker.yaml](worker.yaml), [api.yaml](api.yaml) and [playwright-service.yaml](playwright-service.yaml)
+
+## Install
+```bash
+kubectl apply -f configmap.yaml
+kubectl apply -f secret.yaml
+kubectl apply -f playwright-service.yaml
+kubectl apply -f api.yaml
+kubectl apply -f worker.yaml
+kubectl apply -f redis.yaml
+```
+
+
+# Port Forwarding for Testing
+```bash
+kubectl port-forward svc/api 3002:3002 -n dev
+```
+
+# Delete Firecrawl
+```bash
+kubectl delete -f configmap.yaml
+kubectl delete -f secret.yaml
+kubectl delete -f playwright-service.yaml
+kubectl delete -f api.yaml
+kubectl delete -f worker.yaml
+kubectl delete -f redis.yaml
+```
\ No newline at end of file
diff --git a/examples/kubernetes-cluster-install/api.yaml b/examples/kubernetes-cluster-install/api.yaml
new file mode 100644
index 0000000..cdc69c3
--- /dev/null
+++ b/examples/kubernetes-cluster-install/api.yaml
@@ -0,0 +1,39 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: api
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: api
+ template:
+ metadata:
+ labels:
+ app: api
+ spec:
+ imagePullSecrets:
+ - name: docker-registry-secret
+ containers:
+ - name: api
+ image: ghcr.io/winkk-dev/firecrawl:latest
+ args: [ "pnpm", "run", "start:production" ]
+ ports:
+ - containerPort: 3002
+ envFrom:
+ - configMapRef:
+ name: firecrawl-config
+ - secretRef:
+ name: firecrawl-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: api
+spec:
+ selector:
+ app: api
+ ports:
+ - protocol: TCP
+ port: 3002
+ targetPort: 3002
diff --git a/examples/kubernetes-cluster-install/configmap.yaml b/examples/kubernetes-cluster-install/configmap.yaml
new file mode 100644
index 0000000..b415d56
--- /dev/null
+++ b/examples/kubernetes-cluster-install/configmap.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: firecrawl-config
+data:
+ NUM_WORKERS_PER_QUEUE: "8"
+ PORT: "3002"
+ HOST: "0.0.0.0"
+ REDIS_URL: "redis://redis:6379"
+ PLAYWRIGHT_MICROSERVICE_URL: "http://playwright-service:3000"
+ USE_DB_AUTHENTICATION: "false"
+ SUPABASE_ANON_TOKEN: ""
+ SUPABASE_URL: ""
+ SUPABASE_SERVICE_TOKEN: ""
diff --git a/examples/kubernetes-cluster-install/playwright-service.yaml b/examples/kubernetes-cluster-install/playwright-service.yaml
new file mode 100644
index 0000000..ce79425
--- /dev/null
+++ b/examples/kubernetes-cluster-install/playwright-service.yaml
@@ -0,0 +1,36 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: playwright-service
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: playwright-service
+ template:
+ metadata:
+ labels:
+ app: playwright-service
+ spec:
+ imagePullSecrets:
+ - name: docker-registry-secret
+ containers:
+ - name: playwright-service
+ image: ghcr.io/winkk-dev/firecrawl-playwright:latest
+ ports:
+ - containerPort: 3000
+ envFrom:
+ - configMapRef:
+ name: firecrawl-config
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: playwright-service
+spec:
+ selector:
+ app: playwright-service
+ ports:
+ - protocol: TCP
+ port: 3000
+ targetPort: 3000
diff --git a/examples/kubernetes-cluster-install/redis.yaml b/examples/kubernetes-cluster-install/redis.yaml
new file mode 100644
index 0000000..774d371
--- /dev/null
+++ b/examples/kubernetes-cluster-install/redis.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: redis
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: redis
+ template:
+ metadata:
+ labels:
+ app: redis
+ spec:
+ containers:
+ - name: redis
+ image: redis:alpine
+ args: ["redis-server", "--bind", "0.0.0.0"]
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: redis
+spec:
+ selector:
+ app: redis
+ ports:
+ - protocol: TCP
+ port: 6379
+ targetPort: 6379
diff --git a/examples/kubernetes-cluster-install/secret.yaml b/examples/kubernetes-cluster-install/secret.yaml
new file mode 100644
index 0000000..2be9632
--- /dev/null
+++ b/examples/kubernetes-cluster-install/secret.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: firecrawl-secret
+type: Opaque
+data:
+ OPENAI_API_KEY: ""
+ SLACK_WEBHOOK_URL: ""
+ SERPER_API_KEY: ""
+ LLAMAPARSE_API_KEY: ""
+ LOGTAIL_KEY: ""
+ BULL_AUTH_KEY: ""
+ TEST_API_KEY: ""
+ POSTHOG_API_KEY: ""
+ POSTHOG_HOST: ""
+ SCRAPING_BEE_API_KEY: ""
+ STRIPE_PRICE_ID_STANDARD: ""
+ STRIPE_PRICE_ID_SCALE: ""
+ HYPERDX_API_KEY: ""
+ FIRE_ENGINE_BETA_URL: ""
diff --git a/examples/kubernetes-cluster-install/worker.yaml b/examples/kubernetes-cluster-install/worker.yaml
new file mode 100644
index 0000000..2b3b2e7
--- /dev/null
+++ b/examples/kubernetes-cluster-install/worker.yaml
@@ -0,0 +1,24 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: worker
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: worker
+ template:
+ metadata:
+ labels:
+ app: worker
+ spec:
+ imagePullSecrets:
+ - name: docker-registry-secret
+ containers:
+ - name: worker
+ image: ghcr.io/winkk-dev/firecrawl:latest
+ envFrom:
+ - configMapRef:
+ name: firecrawl-config
+ - secretRef:
+ name: firecrawl-secret
diff --git a/examples/roastmywebsite-example-app/.eslintrc.json b/examples/roastmywebsite-example-app/.eslintrc.json
new file mode 100644
index 0000000..bffb357
--- /dev/null
+++ b/examples/roastmywebsite-example-app/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/examples/roastmywebsite-example-app/.gitignore b/examples/roastmywebsite-example-app/.gitignore
new file mode 100644
index 0000000..037062b
--- /dev/null
+++ b/examples/roastmywebsite-example-app/.gitignore
@@ -0,0 +1,38 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+.env
+node_modules
diff --git a/examples/roastmywebsite-example-app/README.md b/examples/roastmywebsite-example-app/README.md
new file mode 100644
index 0000000..b6ee0e8
--- /dev/null
+++ b/examples/roastmywebsite-example-app/README.md
@@ -0,0 +1,5 @@
+# Roast My Website 🔥
+
+Welcome to Roast My Website, the ultimate tool for putting your website through the wringer! This repository harnesses the power of Firecrawl to scrape and capture screenshots of websites, and then unleashes the latest LLM vision models to mercilessly roast them.
+
+Check it out at roastmywebsite.ai 😈
diff --git a/examples/roastmywebsite-example-app/components.json b/examples/roastmywebsite-example-app/components.json
new file mode 100644
index 0000000..5fe3279
--- /dev/null
+++ b/examples/roastmywebsite-example-app/components.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "src/app/globals.css",
+ "baseColor": "zinc",
+ "cssVariables": false,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
\ No newline at end of file
diff --git a/examples/roastmywebsite-example-app/next.config.mjs b/examples/roastmywebsite-example-app/next.config.mjs
new file mode 100644
index 0000000..c279590
--- /dev/null
+++ b/examples/roastmywebsite-example-app/next.config.mjs
@@ -0,0 +1,11 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ env: {
+ G1: process.env.G1,
+ G2: process.env.G2,
+ G3: process.env.G3,
+ G4: process.env.G4,
+ },
+};
+
+export default nextConfig;
diff --git a/examples/roastmywebsite-example-app/package-lock.json b/examples/roastmywebsite-example-app/package-lock.json
new file mode 100644
index 0000000..9764464
--- /dev/null
+++ b/examples/roastmywebsite-example-app/package-lock.json
@@ -0,0 +1,6617 @@
+{
+ "name": "roastmywebsite",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "roastmywebsite",
+ "version": "0.1.0",
+ "dependencies": {
+ "@dqbd/tiktoken": "^1.0.15",
+ "@headlessui/react": "^2.0.4",
+ "@headlessui/tailwindcss": "^0.2.0",
+ "@mendable/firecrawl-js": "^0.0.21",
+ "@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-dropdown-menu": "^2.0.6",
+ "@radix-ui/react-select": "^2.0.0",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-switch": "^1.0.3",
+ "@remixicon/react": "^4.2.0",
+ "@tremor/react": "^3.17.2",
+ "@vercel/analytics": "^1.3.1",
+ "axios": "^1.7.2",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.1",
+ "cubic-spline": "^3.0.3",
+ "html2canvas": "^1.4.1",
+ "image-size": "^1.1.1",
+ "lucide": "^0.379.0",
+ "lucide-react": "^0.379.0",
+ "next": "14.2.3",
+ "next-themes": "^0.3.0",
+ "openai": "^4.47.3",
+ "react": "^18",
+ "react-dom": "^18",
+ "sonner": "^1.4.41",
+ "tailwind-merge": "^2.3.0",
+ "tailwindcss-animate": "^1.0.7",
+ "tiktoken": "^1.0.15"
+ },
+ "devDependencies": {
+ "@tailwindcss/forms": "^0.5.7",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.3",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.3",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
+ "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@dqbd/tiktoken": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.15.tgz",
+ "integrity": "sha512-a6I67K1xUkuqcuwulobIJiLikkoE7egMaviI1Jg5bxSn2V7QGqXsGE3jTKr8UIOU/o74mAAd5TkeXFNBtaKF4A=="
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
+ "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
+ "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.2.tgz",
+ "integrity": "sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w==",
+ "dependencies": {
+ "@floating-ui/react-dom": "^1.3.0",
+ "aria-hidden": "^1.1.3",
+ "tabbable": "^6.0.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz",
+ "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.2.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
+ "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
+ },
+ "node_modules/@headlessui/react": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.0.4.tgz",
+ "integrity": "sha512-16d/rOLeYsFsmPlRmXGu8DCBzrWD0zV1Ccx3n73wN87yFu8Y9+X04zflv8EJEt9TAYRyLKOmQXUnOnqQl6NgpA==",
+ "dependencies": {
+ "@floating-ui/react": "^0.26.13",
+ "@react-aria/focus": "^3.16.2",
+ "@react-aria/interactions": "^3.21.1",
+ "@tanstack/react-virtual": "3.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^18",
+ "react-dom": "^18"
+ }
+ },
+ "node_modules/@headlessui/react/node_modules/@floating-ui/react": {
+ "version": "0.26.16",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.16.tgz",
+ "integrity": "sha512-HEf43zxZNAI/E781QIVpYSF3K2VH4TTYZpqecjdsFkjsaU1EbaWcM++kw0HXFffj7gDUcBFevX8s0rQGQpxkow==",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.0",
+ "@floating-ui/utils": "^0.2.0",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@headlessui/react/node_modules/@floating-ui/react-dom": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz",
+ "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@headlessui/tailwindcss": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.0.tgz",
+ "integrity": "sha512-fpL830Fln1SykOCboExsWr3JIVeQKieLJ3XytLe/tt1A0XzqUthOftDmjcCYLW62w7mQI7wXcoPXr3tZ9QfGxw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "tailwindcss": "^3.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mendable/firecrawl-js": {
+ "version": "0.0.21",
+ "resolved": "https://registry.npmjs.org/@mendable/firecrawl-js/-/firecrawl-js-0.0.21.tgz",
+ "integrity": "sha512-0geMb1CilorUowVugcVWZeM2a4PCH/iAxCbqPDpt66tEQ40r9217r7U1g3BRHnF/KDA6NvHM7wNIw/HqtUc6uw==",
+ "dependencies": {
+ "axios": "^1.6.8",
+ "zod": "^3.23.8",
+ "zod-to-json-schema": "^3.23.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
+ "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA=="
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz",
+ "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==",
+ "dev": true,
+ "dependencies": {
+ "glob": "10.3.10"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz",
+ "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz",
+ "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz",
+ "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz",
+ "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz",
+ "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz",
+ "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz",
+ "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-ia32-msvc": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz",
+ "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz",
+ "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@radix-ui/number": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
+ "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
+ "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
+ "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
+ "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
+ "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
+ "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dialog": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
+ "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.4",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
+ "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
+ "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-escape-keydown": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dropdown-menu": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz",
+ "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-menu": "2.0.6",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-controllable-state": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
+ "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
+ "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
+ "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-menu": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz",
+ "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.4",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-popper": "1.1.3",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-roving-focus": "1.0.4",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz",
+ "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-rect": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper/node_modules/@floating-ui/react-dom": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz",
+ "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
+ "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
+ "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
+ "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-roving-focus": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz",
+ "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz",
+ "integrity": "sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/number": "1.0.1",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.4",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-popper": "1.1.3",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-visually-hidden": "1.0.3",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
+ "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-switch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
+ "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
+ "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
+ "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
+ "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
+ "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz",
+ "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
+ "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz",
+ "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
+ "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
+ "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@react-aria/focus": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.17.1.tgz",
+ "integrity": "sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==",
+ "dependencies": {
+ "@react-aria/interactions": "^3.21.3",
+ "@react-aria/utils": "^3.24.1",
+ "@react-types/shared": "^3.23.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-aria/interactions": {
+ "version": "3.21.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.21.3.tgz",
+ "integrity": "sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.4",
+ "@react-aria/utils": "^3.24.1",
+ "@react-types/shared": "^3.23.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz",
+ "integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.24.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.1.tgz",
+ "integrity": "sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.4",
+ "@react-stately/utils": "^3.10.1",
+ "@react-types/shared": "^3.23.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz",
+ "integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.23.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz",
+ "integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@remixicon/react": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@remixicon/react/-/react-4.2.0.tgz",
+ "integrity": "sha512-eGhKpZ88OU0qkcY9pJu6khBmItDV82nU130E6C68yc+FbljueHlUYy/4CrJsmf860RIDMay2Rpzl27OSJ81miw==",
+ "peerDependencies": {
+ "react": ">=18.2.0"
+ }
+ },
+ "node_modules/@rushstack/eslint-patch": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz",
+ "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==",
+ "dev": true
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
+ "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/forms": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz",
+ "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==",
+ "dev": true,
+ "dependencies": {
+ "mini-svg-data-uri": "^1.2.3"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
+ }
+ },
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.5.0.tgz",
+ "integrity": "sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.5.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.5.0.tgz",
+ "integrity": "sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tremor/react": {
+ "version": "3.17.2",
+ "resolved": "https://registry.npmjs.org/@tremor/react/-/react-3.17.2.tgz",
+ "integrity": "sha512-zQovHBTonoeJbVk23VAqDeu9yL8N9N2884WYtLr0QvEo4kfC/YUYHkwyg5R7i8ahiJcYbR5zbCG8r9Y4Oqg9qQ==",
+ "dependencies": {
+ "@floating-ui/react": "^0.19.2",
+ "@headlessui/react": "^1.7.19",
+ "@headlessui/tailwindcss": "^0.2.0",
+ "date-fns": "^3.6.0",
+ "react-day-picker": "^8.10.1",
+ "react-transition-state": "^2.1.1",
+ "recharts": "^2.12.7",
+ "tailwind-merge": "^1.14.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/@tremor/react/node_modules/@headlessui/react": {
+ "version": "1.7.19",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz",
+ "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==",
+ "dependencies": {
+ "@tanstack/react-virtual": "^3.0.0-beta.60",
+ "client-only": "^0.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^16 || ^17 || ^18",
+ "react-dom": "^16 || ^17 || ^18"
+ }
+ },
+ "node_modules/@tremor/react/node_modules/tailwind-merge": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz",
+ "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+ "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
+ "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+ "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
+ "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
+ "devOptional": true
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
+ "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
+ "devOptional": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "devOptional": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
+ "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "7.2.0",
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/typescript-estree": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
+ "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
+ "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
+ "dev": true,
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
+ "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.2.0",
+ "@typescript-eslint/visitor-keys": "7.2.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "9.0.3",
+ "semver": "^7.5.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
+ "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.2.0",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^16.0.0 || >=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/@vercel/analytics": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.3.1.tgz",
+ "integrity": "sha512-xhSlYgAuJ6Q4WQGkzYTLmXwhYl39sWjoMA3nHxfkvG+WdBT25c563a7QhwwKivEOZtPJXifYHR1m2ihoisbWyA==",
+ "dependencies": {
+ "server-only": "^0.0.1"
+ },
+ "peerDependencies": {
+ "next": ">= 13",
+ "react": "^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "next": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+ "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/aria-hidden": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
+ "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
+ "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
+ "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.toreversed": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz",
+ "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz",
+ "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.1.0",
+ "es-shim-unscopables": "^1.0.2"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "is-shared-array-buffer": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz",
+ "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+ "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
+ "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001621",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz",
+ "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
+ "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
+ "dependencies": {
+ "clsx": "2.0.0"
+ },
+ "funding": {
+ "url": "https://joebell.co.uk"
+ }
+ },
+ "node_modules/class-variance-authority/node_modules/clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-line-break": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+ "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "node_modules/cubic-spline": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/cubic-spline/-/cubic-spline-3.0.3.tgz",
+ "integrity": "sha512-yAvcHgrpf/k83pZiO4+R2reWOJlufgjpQhmDD3OXa8YMbjmRgjtUK8pcFOCZvJwqXaMD1isZdL7Z4ghqDPN/yw=="
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
+ "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz",
+ "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz",
+ "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/date-fns": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+ "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.16.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz",
+ "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.23.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
+ "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "data-view-buffer": "^1.0.1",
+ "data-view-byte-length": "^1.0.1",
+ "data-view-byte-offset": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.1",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.2",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.9",
+ "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.6",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.15"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz",
+ "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "iterator.prototype": "^1.1.2",
+ "safe-array-concat": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
+ "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "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.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "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.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz",
+ "integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==",
+ "dev": true,
+ "dependencies": {
+ "@next/eslint-plugin-next": "14.2.3",
+ "@rushstack/eslint-patch": "^1.3.3",
+ "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.28.1",
+ "eslint-plugin-jsx-a11y": "^6.7.1",
+ "eslint-plugin-react": "^7.33.2",
+ "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705"
+ },
+ "peerDependencies": {
+ "eslint": "^7.23.0 || ^8.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz",
+ "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.3.4",
+ "enhanced-resolve": "^5.12.0",
+ "eslint-module-utils": "^2.7.4",
+ "fast-glob": "^3.3.1",
+ "get-tsconfig": "^4.5.0",
+ "is-core-module": "^2.11.0",
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
+ "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
+ "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.7",
+ "array.prototype.findlastindex": "^1.2.3",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.8.0",
+ "hasown": "^2.0.0",
+ "is-core-module": "^2.13.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.7",
+ "object.groupby": "^1.0.1",
+ "object.values": "^1.1.7",
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz",
+ "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "aria-query": "^5.3.0",
+ "array-includes": "^3.1.7",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "=4.7.0",
+ "axobject-query": "^3.2.1",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "es-iterator-helpers": "^1.0.15",
+ "hasown": "^2.0.0",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.7",
+ "object.fromentries": "^2.0.7"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.34.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz",
+ "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.7",
+ "array.prototype.findlast": "^1.2.4",
+ "array.prototype.flatmap": "^1.3.2",
+ "array.prototype.toreversed": "^1.1.2",
+ "array.prototype.tosorted": "^1.1.3",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.0.17",
+ "estraverse": "^5.3.0",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.7",
+ "object.fromentries": "^2.0.7",
+ "object.hasown": "^1.1.3",
+ "object.values": "^1.1.7",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.10"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-equals": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
+ "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+ "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data-encoder": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
+ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
+ },
+ "node_modules/formdata-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
+ "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
+ "dependencies": {
+ "node-domexception": "1.0.0",
+ "web-streams-polyfill": "4.0.0-beta.3"
+ },
+ "engines": {
+ "node": ">= 12.20"
+ }
+ },
+ "node_modules/formdata-node/node_modules/web-streams-polyfill": {
+ "version": "4.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+ "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.7.5",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz",
+ "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==",
+ "dev": true,
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/html2canvas": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+ "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+ "dependencies": {
+ "css-line-break": "^2.1.0",
+ "text-segmentation": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/image-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz",
+ "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==",
+ "dependencies": {
+ "queue": "6.0.2"
+ },
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "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.",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
+ "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz",
+ "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==",
+ "dev": true,
+ "dependencies": {
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz",
+ "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz",
+ "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "get-intrinsic": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "reflect.getprototypeof": "^1.0.4",
+ "set-function-name": "^2.0.1"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+ "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
+ "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
+ "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
+ "engines": {
+ "node": "14 || >=16.14"
+ }
+ },
+ "node_modules/lucide": {
+ "version": "0.379.0",
+ "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.379.0.tgz",
+ "integrity": "sha512-2F7ycF+CAZCLusl4iuhhd0nlS5pTMHwdgY4qehZy5qN14tR+db2GX9nhNzhW3mg73wiDlkrue1khgG1BxVNAZg=="
+ },
+ "node_modules/lucide-react": {
+ "version": "0.379.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.379.0.tgz",
+ "integrity": "sha512-KcdeVPqmhRldldAAgptb8FjIunM2x2Zy26ZBh1RsEUcdLIvsEmbcw7KpzFYUy5BbpGeWhPu9Z9J5YXfStiXwhg==",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+ "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mini-svg-data-uri": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
+ "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
+ "dev": true,
+ "bin": {
+ "mini-svg-data-uri": "cli.js"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/next": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz",
+ "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==",
+ "dependencies": {
+ "@next/env": "14.2.3",
+ "@swc/helpers": "0.5.5",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "graceful-fs": "^4.2.11",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.1"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=18.17.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "14.2.3",
+ "@next/swc-darwin-x64": "14.2.3",
+ "@next/swc-linux-arm64-gnu": "14.2.3",
+ "@next/swc-linux-arm64-musl": "14.2.3",
+ "@next/swc-linux-x64-gnu": "14.2.3",
+ "@next/swc-linux-x64-musl": "14.2.3",
+ "@next/swc-win32-arm64-msvc": "14.2.3",
+ "@next/swc-win32-ia32-msvc": "14.2.3",
+ "@next/swc-win32-x64-msvc": "14.2.3"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-themes": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz",
+ "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18",
+ "react-dom": "^16.8 || ^17 || ^18"
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz",
+ "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.hasown": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz",
+ "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz",
+ "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/openai": {
+ "version": "4.47.3",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-4.47.3.tgz",
+ "integrity": "sha512-470d4ibH5kizXflCzgur22GpM4nOjrg7WQ9jTOa3dNKEn248oBy4+pjOyfcFR4V4YUn/YlDNjp6h83PbviCCKQ==",
+ "dependencies": {
+ "@types/node": "^18.11.18",
+ "@types/node-fetch": "^2.6.4",
+ "abort-controller": "^3.0.0",
+ "agentkeepalive": "^4.2.1",
+ "form-data-encoder": "1.7.2",
+ "formdata-node": "^4.3.2",
+ "node-fetch": "^2.6.7",
+ "web-streams-polyfill": "^3.2.1"
+ },
+ "bin": {
+ "openai": "bin/cli"
+ }
+ },
+ "node_modules/openai/node_modules/@types/node": {
+ "version": "18.19.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz",
+ "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/lilconfig": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+ "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
+ "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz",
+ "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
+ "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+ "dependencies": {
+ "inherits": "~2.0.3"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-day-picker": {
+ "version": "8.10.1",
+ "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz",
+ "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/gpbl"
+ },
+ "peerDependencies": {
+ "date-fns": "^2.28.0 || ^3.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-remove-scroll": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
+ "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.3",
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.0",
+ "use-sidecar": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz",
+ "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==",
+ "dependencies": {
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-smooth": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz",
+ "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
+ "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "invariant": "^2.2.4",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/react-transition-state": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.1.1.tgz",
+ "integrity": "sha512-kQx5g1FVu9knoz1T1WkapjUgFz08qQ/g1OmuWGi3/AoEFfS0kStxrPlZx81urjCXdz2d+1DqLpU6TyLW/Ro04Q==",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/recharts": {
+ "version": "2.12.7",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz",
+ "integrity": "sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^16.10.2",
+ "react-smooth": "^4.0.0",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
+ "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.1",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "globalthis": "^1.0.3",
+ "which-builtin-type": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/server-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
+ "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sonner": {
+ "version": "1.4.41",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.41.tgz",
+ "integrity": "sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==",
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
+ "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.7",
+ "regexp.prototype.flags": "^1.5.2",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz",
+ "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz",
+ "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
+ "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+ },
+ "node_modules/tailwind-merge": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz",
+ "integrity": "sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==",
+ "dependencies": {
+ "@babel/runtime": "^7.24.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
+ "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.0",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.0",
+ "lilconfig": "^2.1.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.23",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.1",
+ "postcss-nested": "^6.0.1",
+ "postcss-selector-parser": "^6.0.11",
+ "resolve": "^1.22.2",
+ "sucrase": "^3.32.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tailwindcss-animate": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
+ "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/text-segmentation": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+ "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tiktoken": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.15.tgz",
+ "integrity": "sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw=="
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
+ "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-callback-ref": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz",
+ "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
+ "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/utrie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+ "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+ "dependencies": {
+ "base64-arraybuffer": "^1.0.2"
+ }
+ },
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz",
+ "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==",
+ "dev": true,
+ "dependencies": {
+ "function.prototype.name": "^1.1.5",
+ "has-tostringtag": "^1.0.0",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.0.5",
+ "is-finalizationregistry": "^1.0.2",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.1.4",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.1",
+ "which-typed-array": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yaml": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz",
+ "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz",
+ "integrity": "sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==",
+ "peerDependencies": {
+ "zod": "^3.23.3"
+ }
+ }
+ }
+}
diff --git a/examples/roastmywebsite-example-app/package.json b/examples/roastmywebsite-example-app/package.json
new file mode 100644
index 0000000..c8a30ce
--- /dev/null
+++ b/examples/roastmywebsite-example-app/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "roastmywebsite",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@dqbd/tiktoken": "^1.0.15",
+ "@headlessui/react": "^2.0.4",
+ "@headlessui/tailwindcss": "^0.2.0",
+ "@mendable/firecrawl-js": "^0.0.21",
+ "@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-dropdown-menu": "^2.0.6",
+ "@radix-ui/react-select": "^2.0.0",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-switch": "^1.0.3",
+ "@remixicon/react": "^4.2.0",
+ "@tremor/react": "^3.17.2",
+ "@vercel/analytics": "^1.3.1",
+ "axios": "^1.7.2",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.1",
+ "cubic-spline": "^3.0.3",
+ "html2canvas": "^1.4.1",
+ "image-size": "^1.1.1",
+ "lucide": "^0.379.0",
+ "lucide-react": "^0.379.0",
+ "next": "14.2.3",
+ "next-themes": "^0.3.0",
+ "openai": "^4.47.3",
+ "react": "^18",
+ "react-dom": "^18",
+ "sonner": "^1.4.41",
+ "tailwind-merge": "^2.3.0",
+ "tailwindcss-animate": "^1.0.7",
+ "tiktoken": "^1.0.15"
+ },
+ "devDependencies": {
+ "@tailwindcss/forms": "^0.5.7",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "14.2.3",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.3",
+ "typescript": "^5"
+ }
+}
diff --git a/examples/roastmywebsite-example-app/postcss.config.mjs b/examples/roastmywebsite-example-app/postcss.config.mjs
new file mode 100644
index 0000000..1a69fd2
--- /dev/null
+++ b/examples/roastmywebsite-example-app/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/examples/roastmywebsite-example-app/public/android-chrome-192x192.png b/examples/roastmywebsite-example-app/public/android-chrome-192x192.png
new file mode 100644
index 0000000..8f34d17
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/android-chrome-192x192.png differ
diff --git a/examples/roastmywebsite-example-app/public/android-chrome-512x512.png b/examples/roastmywebsite-example-app/public/android-chrome-512x512.png
new file mode 100644
index 0000000..cf0e8da
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/android-chrome-512x512.png differ
diff --git a/examples/roastmywebsite-example-app/public/apple-touch-icon.png b/examples/roastmywebsite-example-app/public/apple-touch-icon.png
new file mode 100644
index 0000000..6fccaa8
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/apple-touch-icon.png differ
diff --git a/examples/roastmywebsite-example-app/public/bgd.png b/examples/roastmywebsite-example-app/public/bgd.png
new file mode 100644
index 0000000..90bf32d
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/bgd.png differ
diff --git a/examples/roastmywebsite-example-app/public/favicon-16x16.png b/examples/roastmywebsite-example-app/public/favicon-16x16.png
new file mode 100644
index 0000000..7fbf865
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/favicon-16x16.png differ
diff --git a/examples/roastmywebsite-example-app/public/favicon-32x32.png b/examples/roastmywebsite-example-app/public/favicon-32x32.png
new file mode 100644
index 0000000..6dbd6af
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/favicon-32x32.png differ
diff --git a/examples/roastmywebsite-example-app/public/favicon.ico b/examples/roastmywebsite-example-app/public/favicon.ico
new file mode 100644
index 0000000..950c277
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/favicon.ico differ
diff --git a/examples/roastmywebsite-example-app/public/next.svg b/examples/roastmywebsite-example-app/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/examples/roastmywebsite-example-app/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/roastmywebsite-example-app/public/og.png b/examples/roastmywebsite-example-app/public/og.png
new file mode 100644
index 0000000..e832de0
Binary files /dev/null and b/examples/roastmywebsite-example-app/public/og.png differ
diff --git a/examples/roastmywebsite-example-app/public/site.webmanifest b/examples/roastmywebsite-example-app/public/site.webmanifest
new file mode 100644
index 0000000..45dc8a2
--- /dev/null
+++ b/examples/roastmywebsite-example-app/public/site.webmanifest
@@ -0,0 +1 @@
+{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
\ No newline at end of file
diff --git a/examples/roastmywebsite-example-app/public/vercel.svg b/examples/roastmywebsite-example-app/public/vercel.svg
new file mode 100644
index 0000000..d2f8422
--- /dev/null
+++ b/examples/roastmywebsite-example-app/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/roastmywebsite-example-app/src/app/favicon.ico b/examples/roastmywebsite-example-app/src/app/favicon.ico
new file mode 100644
index 0000000..950c277
Binary files /dev/null and b/examples/roastmywebsite-example-app/src/app/favicon.ico differ
diff --git a/examples/roastmywebsite-example-app/src/app/globals.css b/examples/roastmywebsite-example-app/src/app/globals.css
new file mode 100644
index 0000000..e67a573
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/app/globals.css
@@ -0,0 +1,10 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+
+
+.fill-tremor-content-emphasis {
+ fill: rgb(113 113 122) !important;
+}
+
diff --git a/examples/roastmywebsite-example-app/src/app/hooks/useGithubStars.ts b/examples/roastmywebsite-example-app/src/app/hooks/useGithubStars.ts
new file mode 100644
index 0000000..2e3d495
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/app/hooks/useGithubStars.ts
@@ -0,0 +1,5 @@
+export async function useGithubStars() {
+ const res = await fetch("https://api.github.com/repos/mendableai/firecrawl");
+ const data = await res.json();
+ return data.stargazers_count;
+}
diff --git a/examples/roastmywebsite-example-app/src/app/layout.tsx b/examples/roastmywebsite-example-app/src/app/layout.tsx
new file mode 100644
index 0000000..7100d80
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/app/layout.tsx
@@ -0,0 +1,68 @@
+import type { Metadata } from "next";
+import { Gloria_Hallelujah } from "next/font/google";
+import "./globals.css";
+import { Toaster } from "sonner";
+import { Analytics } from "@vercel/analytics/react";
+import { useEffect, useState } from "react";
+import Head from "next/head";
+
+
+const inter = Gloria_Hallelujah({ weight: "400", subsets: ["latin"] });
+// const inter = Inter({ subsets: ["latin"] });
+
+const meta = {
+ title: "Roast My Website",
+ description:
+ "Welcome to Roast My Website, the ultimate tool for putting your website through the wringer! This repository harnesses the power of Firecrawl to scrape and capture screenshots of websites, and then unleashes the latest LLM vision models to mercilessly roast them. 😈",
+ cardImage: "/og.png",
+ robots: "follow, index",
+ favicon: "/favicon.ico",
+ url: "https://www.roastmywebsite.ai/",
+};
+
+export async function generateMetadata(): Promise {
+ return {
+ title: meta.title,
+ description: meta.description,
+ referrer: "origin-when-cross-origin",
+ keywords: ["Roast My Website", "Roast", "Website", "GitHub", "Firecrawl"],
+ authors: [
+ { name: "Roast My Website", url: "https://www.roastmywebsite.ai/" },
+ ],
+ creator: "Roast My Website",
+ publisher: "Roast My Website",
+ robots: meta.robots,
+ icons: { icon: meta.favicon },
+ metadataBase: new URL(meta.url),
+ openGraph: {
+ url: meta.url,
+ title: meta.title,
+ description: meta.description,
+ images: [meta.cardImage],
+ type: "website",
+ siteName: meta.title,
+ },
+ twitter: {
+ card: "summary_large_image",
+ site: "@Vercel",
+ creator: "@Vercel",
+ title: meta.title,
+ description: meta.description,
+ images: [meta.cardImage],
+ },
+ };
+}
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+ {children}
+
+
+
+ );
+}
diff --git a/examples/roastmywebsite-example-app/src/app/page.tsx b/examples/roastmywebsite-example-app/src/app/page.tsx
new file mode 100644
index 0000000..5b28d64
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/app/page.tsx
@@ -0,0 +1,16 @@
+// pages/index.tsx
+import MainComponent from "@/components/main";
+import { useGithubStars } from "./hooks/useGithubStars";
+import GithubButton from "@/components/github-button";
+
+export default async function Home() {
+ const githubStars = await useGithubStars();
+ return (
+
+ );
+}
diff --git a/examples/roastmywebsite-example-app/src/components/github-button.tsx b/examples/roastmywebsite-example-app/src/components/github-button.tsx
new file mode 100644
index 0000000..2795e23
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/github-button.tsx
@@ -0,0 +1,20 @@
+"use client";
+import { Github } from "lucide-react";
+import { Button } from "./ui/button";
+
+export default function GithubButton({ githubStars }: { githubStars: number }) {
+ return (
+ {
+ window.open("https://github.com/mendableai/firecrawl", "_blank");
+ }}
+ variant="outline"
+ size="icon"
+ className="px-3 w-22 gap-2"
+ >
+ {" "}
+ {/* {githubStars ? `Star us on GitHub` : "GitHub"} */}
+ See code on Github
+
+ );
+}
diff --git a/examples/roastmywebsite-example-app/src/components/main.tsx b/examples/roastmywebsite-example-app/src/components/main.tsx
new file mode 100644
index 0000000..63b9a4a
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/main.tsx
@@ -0,0 +1,113 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { Theme, allThemes } from "@/lib/theme";
+import html2canvas from "html2canvas";
+import { Button } from "@/components/ui/button";
+import { Input } from "./ui/input";
+import { Github } from "lucide-react";
+
+export default function MainComponent() {
+ const [roastUrl, setRoastUrl] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [roastData, setRoastData] = useState("");
+
+ const [spiceLevel, setSpiceLevel] = useState(2);
+
+ return (
+
+
+
+
+
+ Roast
+
+ {" "}
+ My Website
+
+
+ setRoastUrl(e.target.value)}
+ />
+ {
+ if (roastUrl) {
+ setLoading(true);
+ try {
+ const response = await fetch(
+ `/api/roastWebsite?url=${encodeURIComponent(
+ roastUrl
+ )}&spiceLevel=${spiceLevel}`
+ );
+ if (!response.ok) {
+ throw new Error(`Error: ${response.statusText}`);
+ }
+ const data = await response.json();
+ setRoastData(data.roastResult);
+ } catch (error) {
+ console.error("Error:", error);
+ } finally {
+ setLoading(false);
+ }
+ }
+ }}
+ >
+ {loading ? "Loading..." : "Get Roasted 🌶️"}
+
+
+
+
+ Choose your roast level:
+
+ setSpiceLevel(Number(e.target.value))}
+ >
+ Mild 🌶️
+ Medium 🌶️🌶️
+ Spicy 🌶️🌶️🌶️
+
+
+ {loading ? (
+
+
Preparing your roast...
+
+ ) : (
+ roastData && (
+
+ )
+ )}
+
+
+
+
+ );
+}
diff --git a/examples/roastmywebsite-example-app/src/components/ui/button.tsx b/examples/roastmywebsite-example-app/src/components/ui/button.tsx
new file mode 100644
index 0000000..bd6bc89
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/button.tsx
@@ -0,0 +1,56 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
+ {
+ variants: {
+ variant: {
+ default: "bg-zinc-900 text-zinc-50 hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90",
+ destructive:
+ "bg-red-500 text-zinc-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90",
+ outline:
+ "border border-zinc-200 bg-white hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
+ secondary:
+ "bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80",
+ ghost: "hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
+ link: "text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/examples/roastmywebsite-example-app/src/components/ui/dialog.tsx b/examples/roastmywebsite-example-app/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..e69a6ad
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/dialog.tsx
@@ -0,0 +1,122 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Dialog = DialogPrimitive.Root
+
+const DialogTrigger = DialogPrimitive.Trigger
+
+const DialogPortal = DialogPrimitive.Portal
+
+const DialogClose = DialogPrimitive.Close
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+DialogContent.displayName = DialogPrimitive.Content.displayName
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogHeader.displayName = "DialogHeader"
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogFooter.displayName = "DialogFooter"
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogTitle.displayName = DialogPrimitive.Title.displayName
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogDescription.displayName = DialogPrimitive.Description.displayName
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+}
diff --git a/examples/roastmywebsite-example-app/src/components/ui/dropdown-menu.tsx b/examples/roastmywebsite-example-app/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..a5bc042
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,200 @@
+"use client"
+
+import * as React from "react"
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
+import { Check, ChevronRight, Circle } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const DropdownMenu = DropdownMenuPrimitive.Root
+
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+
+const DropdownMenuGroup = DropdownMenuPrimitive.Group
+
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+
+const DropdownMenuSub = DropdownMenuPrimitive.Sub
+
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+
+const DropdownMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+))
+DropdownMenuSubTrigger.displayName =
+ DropdownMenuPrimitive.SubTrigger.displayName
+
+const DropdownMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSubContent.displayName =
+ DropdownMenuPrimitive.SubContent.displayName
+
+const DropdownMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+
+const DropdownMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+
+const DropdownMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuCheckboxItem.displayName =
+ DropdownMenuPrimitive.CheckboxItem.displayName
+
+const DropdownMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+
+const DropdownMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+
+const DropdownMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+
+const DropdownMenuShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
+
+export {
+ DropdownMenu,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuGroup,
+ DropdownMenuPortal,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuRadioGroup,
+}
diff --git a/examples/roastmywebsite-example-app/src/components/ui/input.tsx b/examples/roastmywebsite-example-app/src/components/ui/input.tsx
new file mode 100644
index 0000000..c9a4382
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Input.displayName = "Input"
+
+export { Input }
diff --git a/examples/roastmywebsite-example-app/src/components/ui/select.tsx b/examples/roastmywebsite-example-app/src/components/ui/select.tsx
new file mode 100644
index 0000000..610c5de
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/select.tsx
@@ -0,0 +1,160 @@
+"use client"
+
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { Check, ChevronDown, ChevronUp } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ Omit, 'noIcon'> & { noIcon?: boolean }
+>(({ className, children, noIcon, ...props }, ref) => (
+ span]:line-clamp-1 dark:border-zinc-800 dark:bg-zinc-950 dark:ring-offset-zinc-950 dark:placeholder:text-zinc-400 dark:focus:ring-zinc-300",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+ {noIcon ? <>> : }
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/examples/roastmywebsite-example-app/src/components/ui/sonner.tsx b/examples/roastmywebsite-example-app/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..9f71edf
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/sonner.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import { useTheme } from "next-themes"
+import { Toaster as Sonner } from "sonner"
+
+type ToasterProps = React.ComponentProps
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+
+ )
+}
+
+export { Toaster }
diff --git a/examples/roastmywebsite-example-app/src/components/ui/switch.tsx b/examples/roastmywebsite-example-app/src/components/ui/switch.tsx
new file mode 100644
index 0000000..b7e6fb6
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/switch.tsx
@@ -0,0 +1,29 @@
+"use client"
+
+import * as React from "react"
+import * as SwitchPrimitives from "@radix-ui/react-switch"
+
+import { cn } from "@/lib/utils"
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
diff --git a/examples/roastmywebsite-example-app/src/components/ui/textarea.tsx b/examples/roastmywebsite-example-app/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..4a42d9a
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/examples/roastmywebsite-example-app/src/lib/LLM/llm.ts b/examples/roastmywebsite-example-app/src/lib/LLM/llm.ts
new file mode 100644
index 0000000..1d290a3
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/lib/LLM/llm.ts
@@ -0,0 +1,75 @@
+import OpenAI from "openai/index.mjs";
+import { encoding_for_model } from "@dqbd/tiktoken";
+
+/**
+ * Function to generate a roast for a website based on its screenshot and markdown content.
+ * @param roastPrompt - Initial prompt text for the roast.
+ * @param screenshotUrl - URL of the screenshot of the website.
+ * @param content - Raw markdown content of the website.
+ */
+export async function roastPrompt(roastPrompt: string, screenshotUrl: string, content: string) {
+ try {
+ // Initialize OpenAI with the API key from environment variables
+ const openai = new OpenAI({
+ apiKey: process.env.OPEN_AI_KEY
+ });
+
+
+ let contentTruncated = await truncateContentToFit(content, 30000);
+
+ const response = await openai.chat.completions.create({
+ model: "gpt-4o",
+ messages: [
+ {
+ role: "user",
+ content: [
+ { type: "text", text: roastPrompt },
+ { type: "image_url", image_url: { url: screenshotUrl, detail: "low" } },
+ { type: "text", text: contentTruncated }
+ ],
+ },
+ ],
+ });
+ // Return the first choice's message instead of logging it
+ return response.choices[0].message.content;
+ } catch (error) {
+ console.error("Error generating roast:", error);
+ // Assert error as an instance of Error to access message property
+ return `Error generating roast: ${(error as Error).message}`;
+ }
+}
+
+
+
+export function numTokensFromString(message: string, model: string): number {
+ const encoder = encoding_for_model(model as any);
+
+ // Encode the message into tokens
+ const tokens = encoder.encode(message);
+
+ // Free the encoder resources after use
+ encoder.free();
+
+ // Return the number of tokens
+ return tokens.length;
+}
+
+
+
+async function truncateContentToFit(content: string, maxTokens: number): Promise {
+ const modifier = 4;
+
+ let contentTotruncate = content;
+ const numTokens = numTokensFromString(contentTotruncate, "gpt-4");
+
+ if (numTokens > maxTokens) {
+ // trim the document to the maximum number of tokens, tokens != characters
+ contentTotruncate = content.slice(0, (maxTokens * modifier));
+
+ }
+ return contentTotruncate
+}
+
+
+
+
diff --git a/examples/roastmywebsite-example-app/src/lib/LLM/testing_constants.ts b/examples/roastmywebsite-example-app/src/lib/LLM/testing_constants.ts
new file mode 100644
index 0000000..e69de29
diff --git a/examples/roastmywebsite-example-app/src/lib/utils.ts b/examples/roastmywebsite-example-app/src/lib/utils.ts
new file mode 100644
index 0000000..d084cca
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/examples/roastmywebsite-example-app/src/pages/api/roastWebsite.ts b/examples/roastmywebsite-example-app/src/pages/api/roastWebsite.ts
new file mode 100644
index 0000000..0b46988
--- /dev/null
+++ b/examples/roastmywebsite-example-app/src/pages/api/roastWebsite.ts
@@ -0,0 +1,76 @@
+// pages/api/getRepoStars.ts
+import { NextApiRequest, NextApiResponse } from 'next';
+import FirecrawlApp from '@mendable/firecrawl-js';
+import { roastPrompt } from '../../lib/LLM/llm';
+
+
+const mildPrompt = "Give me a light hearted roast for this website:"
+const mediumPrompt = "Give me a roast for this website:"
+const spicyPrompt ="Roast this website. The person who made this website wants to be roasted really hard. I mean REALLY hard, don't hold back, and don't worry about hurting their feelings. Be mean but concisely mean. No more than 2 paragraphs."
+
+
+async function getScreenShotAndHtml(url: string) {
+ const app = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY });
+ const scrapeResult = await app.scrapeUrl(url, {
+ pageOptions: {
+ includeHtml: true,
+ screenshot: true
+ }
+ });
+ return {
+ screenshotUrl: scrapeResult.data.metadata.screenshot,
+ htmlContent: scrapeResult.data.html
+ };
+}
+
+
+export default async function handler(req: NextApiRequest, res: NextApiResponse) {
+ const rawUrl = typeof req.query.url === 'string' ? req.query.url : null;
+ const spiceLevel = typeof req.query.spiceLevel === 'string' ? parseInt(req.query.spiceLevel, 10) : null;
+
+ if (!rawUrl || spiceLevel === null || isNaN(spiceLevel)) {
+ res.status(400).json({ error: 'Invalid query parameters' });
+ return;
+ }
+
+ try {
+ const { screenshotUrl, htmlContent } = await getScreenShotAndHtml(rawUrl);
+
+ // Define a roast prompt message
+ let roastMessage: string;
+
+ // Determine the roast message based on the spice level
+ switch (spiceLevel) {
+ case 1:
+ roastMessage = mildPrompt;
+ break;
+ case 2:
+ roastMessage = mediumPrompt;
+ break;
+ case 3:
+ roastMessage = spicyPrompt;
+ break;
+ default:
+ // If an invalid spice level is provided, default to mild roast
+ roastMessage = mildPrompt;
+ res.status(400).json({ error: 'Invalid spice level' });
+ return;
+ }
+
+
+
+ // Convert HTML content to a markdown-like format for the roast generation
+ // This is a simplified conversion, assuming HTML content is already sanitized and suitable for direct usage
+
+ // Call the roastPrompt function to generate a roast
+ const roastResult = await roastPrompt(roastMessage, screenshotUrl, htmlContent);
+
+ // Log the roast result for debugging
+ // console.log("Roast Result:", roastResult);
+
+ res.status(200).json({ screenshotUrl, htmlContent, roastResult});
+ } catch (error: any) {
+ res.status(500).json({ error: error.message });
+ }
+}
+
diff --git a/examples/roastmywebsite-example-app/tailwind.config.ts b/examples/roastmywebsite-example-app/tailwind.config.ts
new file mode 100644
index 0000000..470e82c
--- /dev/null
+++ b/examples/roastmywebsite-example-app/tailwind.config.ts
@@ -0,0 +1,137 @@
+import type { Config } from 'tailwindcss';
+import colors from 'tailwindcss/colors';
+
+const config: Config = {
+ darkMode: 'class',
+ content: [
+ './src/**/*.{js,ts,jsx,tsx}',
+
+ // Path to Tremor module
+ './node_modules/@tremor/**/*.{js,ts,jsx,tsx}',
+ ],
+ theme: {
+ transparent: 'transparent',
+ current: 'currentColor',
+ extend: {
+ colors: {
+ // light mode
+ tremor: {
+ brand: {
+ faint: colors.blue[50],
+ muted: colors.blue[200],
+ subtle: colors.blue[400],
+ DEFAULT: colors.blue[500],
+ emphasis: colors.blue[700],
+ inverted: colors.white,
+ },
+ background: {
+ muted: colors.gray[50],
+ subtle: colors.gray[100],
+ DEFAULT: colors.white,
+ emphasis: colors.gray[700],
+ },
+ border: {
+ DEFAULT: colors.gray[200],
+ },
+ ring: {
+ DEFAULT: colors.gray[200],
+ },
+ content: {
+ subtle: colors.gray[400],
+ DEFAULT: colors.gray[500],
+ emphasis: colors.gray[700],
+ strong: colors.gray[900],
+ inverted: colors.white,
+ },
+ },
+ // dark mode
+ 'dark-tremor': {
+ brand: {
+ faint: '#0B1229',
+ muted: colors.blue[950],
+ subtle: colors.blue[800],
+ DEFAULT: colors.blue[500],
+ emphasis: colors.blue[400],
+ inverted: colors.blue[950],
+ },
+ background: {
+ muted: '#131A2B',
+ subtle: colors.gray[800],
+ DEFAULT: colors.gray[900],
+ emphasis: colors.gray[300],
+ },
+ border: {
+ DEFAULT: colors.gray[800],
+ },
+ ring: {
+ DEFAULT: colors.gray[800],
+ },
+ content: {
+ subtle: colors.gray[600],
+ DEFAULT: colors.gray[500],
+ emphasis: colors.gray[200],
+ strong: colors.gray[50],
+ inverted: colors.gray[950],
+ },
+ },
+ },
+ boxShadow: {
+ // light
+ 'tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ 'tremor-card':
+ '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ 'tremor-dropdown':
+ '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
+ // dark
+ 'dark-tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ 'dark-tremor-card':
+ '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ 'dark-tremor-dropdown':
+ '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
+ },
+ borderRadius: {
+ 'tremor-small': '0.375rem',
+ 'tremor-default': '0.5rem',
+ 'tremor-full': '9999px',
+ },
+ fontSize: {
+ 'tremor-label': ['0.75rem', { lineHeight: '1rem' }],
+ 'tremor-default': ['0.875rem', { lineHeight: '1.25rem' }],
+ 'tremor-title': ['1.125rem', { lineHeight: '1.75rem' }],
+ 'tremor-metric': ['1.875rem', { lineHeight: '2.25rem' }],
+ },
+ },
+ },
+ safelist: [
+ {
+ pattern:
+ /^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ variants: ['hover', 'ui-selected'],
+ },
+ {
+ pattern:
+ /^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ variants: ['hover', 'ui-selected'],
+ },
+ {
+ pattern:
+ /^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ variants: ['hover', 'ui-selected'],
+ },
+ {
+ pattern:
+ /^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ },
+ {
+ pattern:
+ /^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ },
+ {
+ pattern:
+ /^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
+ },
+ ],
+ plugins: [require('@headlessui/tailwindcss'), require('@tailwindcss/forms')],
+};
+
+export default config;
\ No newline at end of file
diff --git a/examples/roastmywebsite-example-app/tsconfig.json b/examples/roastmywebsite-example-app/tsconfig.json
new file mode 100644
index 0000000..7b28589
--- /dev/null
+++ b/examples/roastmywebsite-example-app/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/tutorials/contradiction-testing-using-llms.mdx b/examples/web-data-contradiction-testing-using-llms.mdx
similarity index 100%
rename from tutorials/contradiction-testing-using-llms.mdx
rename to examples/web-data-contradiction-testing-using-llms.mdx
diff --git a/tutorials/data-extraction-using-llms.mdx b/examples/web-data-extraction-using-llms.mdx
similarity index 100%
rename from tutorials/data-extraction-using-llms.mdx
rename to examples/web-data-extraction-using-llms.mdx
diff --git a/tutorials/rag-llama3.mdx b/examples/web-data-rag--with-llama3.mdx
similarity index 100%
rename from tutorials/rag-llama3.mdx
rename to examples/web-data-rag--with-llama3.mdx