commit 6fe4fe55f308b06c7975b61ba6e1bb63535a7bc2 Author: LimeDrive Date: Tue Jul 9 04:13:29 2024 +0200 init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f536d28 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,150 @@ +### Python template + +deploy/ +.idea/ +.vscode/ +.git/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# database +*.db + +# poetry +poetry.toml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..16c88e7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[Bug]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Your system info (please complete the following information):** + - OS: [e.g. Ubuntu] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + - Docker config ? + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6269982 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml new file mode 100644 index 0000000..2668531 --- /dev/null +++ b/.github/workflows/docker-develop.yml @@ -0,0 +1,38 @@ +name: Build and Push Docker image to GitHub Packages on Dev Branch + +on: + push: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: all + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GHCR_PAT }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: source/. + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ghcr.io/limedrive/stremio-jackett:develop diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6813cf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,148 @@ +### Python template +.idea/ +.vscode/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +*.log.zip +local_settings.py +*.sqlite3 +*.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# database +*.db + +# apple +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..59b25c6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.11.9-slim-bullseye + +RUN pip install poetry +WORKDIR /app + +COPY pyproject.toml poetry.lock ./ + +RUN poetry config virtualenvs.create false \ + && poetry install --only main --no-interaction --no-ansi --no-root + +COPY . . + +ARG GUNICORN_PORT=8080 +ENV EXPOSE_PORT=${GUNICORN_PORT} + +EXPOSE ${EXPOSE_PORT} + +CMD ["python", "-m", "stream_fusion"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4b890b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 LimeCat on the Hub's + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..10d6017 --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +# StreamFusion + +## Description + +StreamFusion is an advanced plugin for Stremio that significantly enhances its streaming capabilities. It leverages torrent indexers and Jackett to integrate cached torrent sources from popular debrid services, offering a smooth and comprehensive streaming experience. This application acts as a bridge between Stremio, torrent indexers, and debrid services, providing users with a wide range of content options. + +## Key Features + +- **Torrent Indexer Integration**: Utilizes various torrent indexers and Jackett to source content. +- **Debrid Service Integration**: Adds cached torrents from RealDebrid, AllDebrid, and Premiumize as sources for Stremio. +- **Direct Streaming**: Uses indexers for direct streaming when cached torrents are unavailable. +- **Advanced Cache Management**: + - Utilizes Redis for efficient caching. + - Community cache for sharing cached torrent searches. +- **Zilean API**: Integration of the DMM Zilean API to obtain cached torrent hashes. +- **Stream Proxification**: All streams originate from a single IP address. +- **Download Management**: Capable of managing torrent downloads on debrid services. + +## Installation + +[Installation instructions to be added] + +## Configuration + +[Configuration details to be added] + +## Usage + +[Usage guide to be added] + +## Contributing + +Contributions are welcome! Please refer to our contribution guidelines for more details. + +```bash +tree -I '*.pyc' -I '__pycache__' +. +├── Dockerfile +├── README.md +├── deploy +│   └── docker-compose.yml +├── poetry.lock +├── poetry.toml +├── pyproject.toml +└── stream_fusion + ├── __init__.py + ├── __main__.py + ├── constants.py + ├── gunicorn_runner.py + ├── logging_config.py + ├── services + │   ├── redis + │   │   └── redis_config.py + │   └── security_db + │   ├── __init__.py + │   └── _sqlite_access.py + ├── settings.py + ├── static + ├── templates + │   ├── config.js + │   └── index.html + ├── utils + │   ├── cache + │   │   ├── cache_base.py + │   │   └── local_redis.py + │   ├── cache.py + │   ├── debrid + │   │   ├── alldebrid.py + │   │   ├── base_debrid.py + │   │   ├── get_debrid_service.py + │   │   ├── premiumize.py + │   │   └── realdebrid.py + │   ├── detection.py + │   ├── filter + │   │   ├── base_filter.py + │   │   ├── language_filter.py + │   │   ├── max_size_filter.py + │   │   ├── quality_exclusion_filter.py + │   │   ├── results_per_quality_filter.py + │   │   └── title_exclusion_filter.py + │   ├── filter_results.py + │   ├── general.py + │   ├── jackett + │   │   ├── jackett_indexer.py + │   │   ├── jackett_result.py + │   │   └── jackett_service.py + │   ├── metdata + │   │   ├── cinemeta.py + │   │   ├── metadata_provider_base.py + │   │   └── tmdb.py + │   ├── models + │   │   ├── media.py + │   │   ├── movie.py + │   │   └── series.py + │   ├── parse_config.py + │   ├── security + │   │   ├── __init__.py + │   │   ├── security_api_key.py + │   │   └── security_secret.py + │   ├── string_encoding.py + │   ├── torrent + │   │   ├── torrent_item.py + │   │   ├── torrent_service.py + │   │   └── torrent_smart_container.py + │   └── zilean + │   ├── zilean_result.py + │   └── zilean_service.py + ├── version.py + ├── videos + │   └── nocache.mp4 + └── web + ├── __init__.py + ├── api + │   ├── __init__.py + │   ├── auth + │   │   ├── __init__.py + │   │   ├── schemas.py + │   │   └── views.py + │   ├── docs + │   │   ├── __init__.py + │   │   └── views.py + │   ├── monitoring + │   │   ├── __init__.py + │   │   └── views.py + │   └── router.py + ├── application.py + ├── lifetime.py + ├── playback + │   ├── __init__.py + │   ├── router.py + │   └── stream + │   ├── __init__.py + │   ├── schemas.py + │   └── views.py + └── root + ├── __init__.py + ├── config + │   ├── __init__.py + │   ├── schemas.py + │   └── views.py + ├── router.py + └── search + ├── __init__.py + ├── schemas.py + ├── stremio_parser.py + └── views.py + +29 directories, 81 files +``` + +## License + +[License information to be added] + +## Disclaimer + +This project is intended for educational and research purposes only. Users are responsible for their use of the software and must comply with local copyright and intellectual property laws. + +## Contact + +[Contact information to be added] + +--- + +StreamFusion is a project derived from Stremio-Jackett, rewritten and improved to offer a better user experience and extended functionalities. \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..2fed88f --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1 @@ +# TODO: \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..7308bf7 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2463 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "aiocron" +version = "1.8" +description = "Crontabs for asyncio" +optional = false +python-versions = "*" +files = [ + {file = "aiocron-1.8-py3-none-any.whl", hash = "sha256:b6313214c311b62aa2220e872b94139b648631b3103d062ef29e5d3230ddce6d"}, + {file = "aiocron-1.8.tar.gz", hash = "sha256:48546513faf2eb7901e65a64eba7b653c80106ed00ed9ca3419c3d10b6555a01"}, +] + +[package.dependencies] +croniter = "*" +tzlocal = "*" + +[package.extras] +test = ["coverage"] + +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "aiosqlite" +version = "0.20.0" +description = "asyncio bridge to the standard sqlite3 module" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, + {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, +] + +[package.dependencies] +typing_extensions = ">=4.0" + +[package.extras] +dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "arrow" +version = "1.3.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, + {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +types-python-dateutil = ">=2.8.10" + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "bencode-py" +version = "4.0.0" +description = "Simple bencode parser (for Python 2, Python 3 and PyPy)" +optional = false +python-versions = "*" +files = [ + {file = "bencode.py-4.0.0-py2.py3-none-any.whl", hash = "sha256:99c06a55764e85ffe81622fdf9ee78bd737bad3ea61d119784a54bb28860d962"}, + {file = "bencode.py-4.0.0.tar.gz", hash = "sha256:2a24ccda1725a51a650893d0b63260138359eaa299bb6e7a09961350a2a6e05c"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "croniter" +version = "2.0.5" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6" +files = [ + {file = "croniter-2.0.5-py2.py3-none-any.whl", hash = "sha256:fdbb44920944045cc323db54599b321325141d82d14fa7453bc0699826bbe9ed"}, + {file = "croniter-2.0.5.tar.gz", hash = "sha256:f1f8ca0af64212fbe99b1bee125ee5a1b53a9c1b433968d8bca8817b79d237f3"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.2.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "fastapi" +version = "0.111.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, +] + +[package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.4" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, +] + +[package.dependencies] +typer = ">=0.12.3" + +[package.extras] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "22.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, + {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonpickle" +version = "3.2.2" +description = "Python library for serializing arbitrary object graphs into JSON" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonpickle-3.2.2-py3-none-any.whl", hash = "sha256:87cd82d237fd72c5a34970e7222dddc0accc13fddf49af84111887ed9a9445aa"}, + {file = "jsonpickle-3.2.2.tar.gz", hash = "sha256:d425fd2b8afe9f5d7d57205153403fbf897782204437882a477e8eed60930f8c"}, +] + +[package.extras] +docs = ["furo", "rst.linker (>=1.9)", "sphinx"] +packaging = ["build", "twine"] +testing = ["bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy", "scipy (>=1.9.3)", "simplejson", "sqlalchemy", "ujson"] + +[[package]] +name = "levenshtein" +version = "0.25.1" +description = "Python extension for computing string edit distances and similarities." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb4d1ec9f2dcbde1757c4b7fb65b8682bc2de45b9552e201988f287548b7abdf"}, + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4d9fa3affef48a7e727cdbd0d9502cd060da86f34d8b3627edd769d347570e2"}, + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1b6cd186e58196ff8b402565317e9346b408d0c04fa0ed12ce4868c0fcb6d03"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82637ef5428384dd1812849dd7328992819bf0c4a20bff0a3b3ee806821af7ed"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e73656da6cc3e32a6e4bcd48562fcb64599ef124997f2c91f5320d7f1532c069"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5abff796f92cdfba69b9cbf6527afae918d0e95cbfac000bd84017f74e0bd427"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38827d82f2ca9cb755da6f03e686866f2f411280db005f4304272378412b4cba"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b989df1e3231261a87d68dfa001a2070771e178b09650f9cf99a20e3d3abc28"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2011d3b3897d438a2f88ef7aed7747f28739cae8538ec7c18c33dd989930c7a0"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c375b33ec7acc1c6855e8ee8c7c8ac6262576ffed484ff5c556695527f49686"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ce0cb9dd012ef1bf4d5b9d40603e7709b6581aec5acd32fcea9b371b294ca7aa"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9da9ecb81bae67d784defed7274f894011259b038ec31f2339c4958157970115"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3bd7be5dbe5f4a1b691f381e39512927b39d1e195bd0ad61f9bf217a25bf36c9"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win32.whl", hash = "sha256:f6abb9ced98261de67eb495b95e1d2325fa42b0344ed5763f7c0f36ee2e2bdba"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:97581af3e0a6d359af85c6cf06e51f77f4d635f7109ff7f8ed7fd634d8d8c923"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:9ba008f490788c6d8d5a10735fcf83559965be97e4ef0812db388a84b1cc736a"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f57d9cf06dac55c2d2f01f0d06e32acc074ab9a902921dc8fddccfb385053ad5"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:22b60c6d791f4ca67a3686b557ddb2a48de203dae5214f220f9dddaab17f44bb"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d0444ee62eccf1e6cedc7c5bc01a9face6ff70cc8afa3f3ca9340e4e16f601a4"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e8758be8221a274c83924bae8dd8f42041792565a3c3bdd3c10e3f9b4a5f94e"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:147221cfb1d03ed81d22fdd2a4c7fc2112062941b689e027a30d2b75bbced4a3"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a454d5bc4f4a289f5471418788517cc122fcc00d5a8aba78c54d7984840655a2"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c25f3778bbac78286bef2df0ca80f50517b42b951af0a5ddaec514412f79fac"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:181486cf465aff934694cc9a19f3898a1d28025a9a5f80fc1608217e7cd1c799"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8db9f672a5d150706648b37b044dba61f36ab7216c6a121cebbb2899d7dfaa3"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f2a69fe5ddea586d439f9a50d0c51952982f6c0db0e3573b167aa17e6d1dfc48"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3b684675a3bd35efa6997856e73f36c8a41ef62519e0267dcbeefd15e26cae71"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:cc707ef7edb71f6bf8339198b929ead87c022c78040e41668a4db68360129cef"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:41512c436b8c691326e2d07786d906cba0e92b5e3f455bf338befb302a0ca76d"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win32.whl", hash = "sha256:2a3830175c01ade832ba0736091283f14a6506a06ffe8c846f66d9fbca91562f"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:9e0af4e6e023e0c8f79af1d1ca5f289094eb91201f08ad90f426d71e4ae84052"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:38e5d9a1d737d7b49fa17d6a4c03a0359288154bf46dc93b29403a9dd0cd1a7d"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4a40fa16ecd0bf9e557db67131aabeea957f82fe3e8df342aa413994c710c34e"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4f7d2045d5927cffa65a0ac671c263edbfb17d880fdce2d358cd0bda9bcf2b6d"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f96590539f9815be70e330b4d2efcce0219db31db5a22fffe99565192f5662"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d78512dd25b572046ff86d8903bec283c373063349f8243430866b6a9946425"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c161f24a1b216e8555c874c7dd70c1a0d98f783f252a16c9face920a8b8a6f3e"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06ebbfd010a00490795f478d18d7fa2ffc79c9c03fc03b678081f31764d16bab"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa9ec0a4489ebfb25a9ec2cba064ed68d0d2485b8bc8b7203f84a7874755e0f"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26408938a6db7b252824a701545d50dc9cdd7a3e4c7ee70834cca17953b76ad8"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:330ec2faff957281f4e6a1a8c88286d1453e1d73ee273ea0f937e0c9281c2156"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9115d1b08626dfdea6f3955cb49ba5a578f7223205f80ead0038d6fc0442ce13"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:bbd602edab758e93a5c67bf0d8322f374a47765f1cdb6babaf593a64dc9633ad"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b930b4df32cd3aabbed0e9f0c4fdd1ea4090a5c022ba9f1ae4ab70ccf1cf897a"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd66fb51f88a3f73a802e1ff19a14978ddc9fbcb7ce3a667ca34f95ef54e0e44"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win32.whl", hash = "sha256:386de94bd1937a16ae3c8f8b7dd2eff1b733994ecf56ce4d05dfdd0e776d0261"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ee1902153d47886c9787598a4a5c324ce7fde44d44daa34fcf3652ac0de21bc"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:b56a7e7676093c3aee50402226f4079b15bd21b5b8f1820f9d6d63fe99dc4927"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6b5dfdf6a0e2f35fd155d4c26b03398499c24aba7bc5db40245789c46ad35c04"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355ff797f704459ddd8b95354d699d0d0642348636c92d5e67b49be4b0e6112b"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:933b827a3b721210fff522f3dca9572f9f374a0e88fa3a6c7ee3164406ae7794"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be1da669a240f272d904ab452ad0a1603452e190f4e03e886e6b3a9904152b89"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:265cbd78962503a26f2bea096258a3b70b279bb1a74a525c671d3ee43a190f9c"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63cc4d53a35e673b12b721a58b197b4a65734688fb72aa1987ce63ed612dca96"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75fee0c471b8799c70dad9d0d5b70f1f820249257f9617601c71b6c1b37bee92"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:045d6b0db124fbd37379b2b91f6d0786c2d9220e7a848e2dd31b99509a321240"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:db7a2e9c51ac9cc2fd5679484f1eac6e0ab2085cb181240445f7fbf10df73230"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c379c588aa0d93d4607db7eb225fd683263d49669b1bbe49e28c978aa6a4305d"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:966dd00424df7f69b78da02a29b530fbb6c1728e9002a2925ed7edf26b231924"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:09daa6b068709cc1e68b670a706d928ed8f0b179a26161dd04b3911d9f757525"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d6bed0792635081accf70a7e11cfece986f744fddf46ce26808cd8bfc067e430"}, + {file = "Levenshtein-0.25.1-cp38-cp38-win32.whl", hash = "sha256:28e7b7faf5a745a690d1b1706ab82a76bbe9fa6b729d826f0cfdd24fd7c19740"}, + {file = "Levenshtein-0.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:8ca0cc9b9e07316b5904f158d5cfa340d55b4a3566ac98eaac9f087c6efb9a1a"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:45682cdb3ac4a5465c01b2dce483bdaa1d5dcd1a1359fab37d26165b027d3de2"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8dc3e63c4cd746ec162a4cd744c6dde857e84aaf8c397daa46359c3d54e6219"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01ad1eb09933a499a49923e74e05b1428ca4ef37fed32965fef23f1334a11563"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbb4e8c4b8b7bbe0e1aa64710b806b6c3f31d93cb14969ae2c0eff0f3a592db8"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48d1fe224b365975002e3e2ea947cbb91d2936a16297859b71c4abe8a39932c"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a164df16d876aab0a400f72aeac870ea97947ea44777c89330e9a16c7dc5cc0e"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d3bcedcf64be6ceca423f6cfe29184a36d7c4cbac199fdc9a0a5ec7196cf5"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdaf62d637bef6711d6f3457e2684faab53b2db2ed53c05bc0dc856464c74742"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:af9de3b5f8f5f3530cfd97daab9ab480d1b121ef34d8c0aa5bab0c645eae219e"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:78fba73c352383b356a30c4674e39f086ffef7122fa625e7550b98be2392d387"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:9e0df0dcea3943321398f72e330c089b5d5447318310db6f17f5421642f3ade6"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:387f768bb201b9bc45f0f49557e2fb9a3774d9d087457bab972162dcd4fd352b"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dcf931b64311039b43495715e9b795fbd97ab44ba3dd6bf24360b15e4e87649"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win32.whl", hash = "sha256:2449f8668c0bd62a2b305a5e797348984c06ac20903b38b3bab74e55671ddd51"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:28803fd6ec7b58065621f5ec0d24e44e2a7dc4842b64dcab690cb0a7ea545210"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win_arm64.whl", hash = "sha256:0b074d452dff8ee86b5bdb6031aa32bb2ed3c8469a56718af5e010b9bb5124dc"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e9e060ef3925a68aeb12276f0e524fb1264592803d562ec0306c7c3f5c68eae0"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f84b84049318d44722db307c448f9dcb8d27c73525a378e901189a94889ba61"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e23fdf330cb185a0c7913ca5bd73a189dfd1742eae3a82e31ed8688b191800"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06958e4a81ea0f0b2b7768a2ad05bcd50a9ad04c4d521dd37d5730ff12decdc"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2ea7c34ec22b2fce21299b0caa6dde6bdebafcc2970e265853c9cfea8d1186da"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fddc0ccbdd94f57aa32e2eb3ac8310d08df2e175943dc20b3e1fc7a115850af4"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d52249cb3448bfe661d3d7db3a6673e835c7f37b30b0aeac499a1601bae873d"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8dd4c201b15f8c1e612f9074335392c8208ac147acbce09aff04e3974bf9b16"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23a4d95ce9d44161c7aa87ab76ad6056bc1093c461c60c097054a46dc957991f"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:65eea8a9c33037b23069dca4b3bc310e3c28ca53f60ec0c958d15c0952ba39fa"}, + {file = "Levenshtein-0.25.1.tar.gz", hash = "sha256:2df14471c778c75ffbd59cb64bbecfd4b0ef320ef9f80e4804764be7d5678980"}, +] + +[package.dependencies] +rapidfuzz = ">=3.8.0,<4.0.0" + +[[package]] +name = "loguru" +version = "0.7.2" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "orjson" +version = "3.10.6" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, + {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, + {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, + {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, + {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, + {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, + {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, + {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, + {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, + {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, + {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, + {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, + {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, + {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, + {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, + {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "parse-torrent-title" +version = "2.8.1" +description = "Extract media information from torrent-like filename" +optional = false +python-versions = "*" +files = [ + {file = "parse-torrent-title-2.8.1.tar.gz", hash = "sha256:939647c4bfacd2b3b878503a7bf79fcdbb11e74a895a4550eb84406d62742457"}, + {file = "parse_torrent_title-2.8.1-py2-none-any.whl", hash = "sha256:0467809b5004065e29c60dfe120499f44832c0be49dc0e7583a1325dd81bbc88"}, + {file = "parse_torrent_title-2.8.1-py3-none-any.whl", hash = "sha256:b96ee593b25fbf07150066d9d8de488b17c15c92b8b05745f984ff47db7f9fcb"}, +] + +[[package]] +name = "parsett" +version = "0.2.4" +description = "PTT" +optional = false +python-versions = "<4.0,>=3.11" +files = [ + {file = "parsett-0.2.4-py3-none-any.whl", hash = "sha256:2a47634f2616fffe6e0fc574439692c3d938442daaea51320ecabe002e995aae"}, + {file = "parsett-0.2.4.tar.gz", hash = "sha256:aa609f253841fd8586414428965c4fcaae436a3ca655c0a572359f921b56f108"}, +] + +[package.dependencies] +arrow = ">=1.3.0,<2.0.0" +regex = ">=2023.12.25,<2024.0.0" + +[[package]] +name = "pydantic" +version = "2.8.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.20.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.3.4" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.3.4-py3-none-any.whl", hash = "sha256:11ad8bacb68a045f00e4f862c7a718c8a9ec766aa8fd4c32e39a0594b207b53a"}, + {file = "pydantic_settings-2.3.4.tar.gz", hash = "sha256:c5802e3d62b78e82522319bbc9b8f8ffb28ad1c988a99311d04f2a6051fca0a7"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rank-torrent-name" +version = "0.2.23" +description = "Parse Torrents using PTN and Rank them according to your preferences!" +optional = false +python-versions = "<4.0,>=3.11" +files = [ + {file = "rank_torrent_name-0.2.23-py3-none-any.whl", hash = "sha256:61b96db32e6d37dec4101c86654d266bde04628c3ac983fe71b99c4614b4d9eb"}, + {file = "rank_torrent_name-0.2.23.tar.gz", hash = "sha256:50a66c789ef74c9d0f7bdb84a3921e9bcf6b3d2f5ae3ffcce44d3dfac2590c00"}, +] + +[package.dependencies] +levenshtein = ">=0.25.0,<0.26.0" +parse-torrent-title = ">=2.8.1,<3.0.0" +parsett = ">=0.2.4,<0.3.0" +pydantic = ">=2.6.3,<3.0.0" +regex = ">=2023.12.25,<2024.0.0" + +[[package]] +name = "rapidfuzz" +version = "3.9.4" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9b9793c19bdf38656c8eaefbcf4549d798572dadd70581379e666035c9df781"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:015b5080b999404fe06ec2cb4f40b0be62f0710c926ab41e82dfbc28e80675b4"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc5ceca9c1e1663f3e6c23fb89a311f69b7615a40ddd7645e3435bf3082688a"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1424e238bc3f20e1759db1e0afb48a988a9ece183724bef91ea2a291c0b92a95"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed01378f605aa1f449bee82cd9c83772883120d6483e90aa6c5a4ce95dc5c3aa"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb26d412271e5a76cdee1c2d6bf9881310665d3fe43b882d0ed24edfcb891a84"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f37e9e1f17be193c41a31c864ad4cd3ebd2b40780db11cd5c04abf2bcf4201b"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d070ec5cf96b927c4dc5133c598c7ff6db3b833b363b2919b13417f1002560bc"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:10e61bb7bc807968cef09a0e32ce253711a2d450a4dce7841d21d45330ffdb24"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:31a2fc60bb2c7face4140010a7aeeafed18b4f9cdfa495cc644a68a8c60d1ff7"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fbebf1791a71a2e89f5c12b78abddc018354d5859e305ec3372fdae14f80a826"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aee9fc9e3bb488d040afc590c0a7904597bf4ccd50d1491c3f4a5e7e67e6cd2c"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win32.whl", hash = "sha256:005a02688a51c7d2451a2d41c79d737aa326ff54167211b78a383fc2aace2c2c"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:3a2e75e41ee3274754d3b2163cc6c82cd95b892a85ab031f57112e09da36455f"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win_arm64.whl", hash = "sha256:2c99d355f37f2b289e978e761f2f8efeedc2b14f4751d9ff7ee344a9a5ca98d9"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:07141aa6099e39d48637ce72a25b893fc1e433c50b3e837c75d8edf99e0c63e1"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db1664eaff5d7d0f2542dd9c25d272478deaf2c8412e4ad93770e2e2d828e175"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc01a223f6605737bec3202e94dcb1a449b6c76d46082cfc4aa980f2a60fd40e"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1869c42e73e2a8910b479be204fa736418741b63ea2325f9cc583c30f2ded41a"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62ea7007941fb2795fff305ac858f3521ec694c829d5126e8f52a3e92ae75526"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:698e992436bf7f0afc750690c301215a36ff952a6dcd62882ec13b9a1ebf7a39"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b76f611935f15a209d3730c360c56b6df8911a9e81e6a38022efbfb96e433bab"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129627d730db2e11f76169344a032f4e3883d34f20829419916df31d6d1338b1"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:90a82143c14e9a14b723a118c9ef8d1bbc0c5a16b1ac622a1e6c916caff44dd8"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ded58612fe3b0e0d06e935eaeaf5a9fd27da8ba9ed3e2596307f40351923bf72"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f16f5d1c4f02fab18366f2d703391fcdbd87c944ea10736ca1dc3d70d8bd2d8b"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:26aa7eece23e0df55fb75fbc2a8fb678322e07c77d1fd0e9540496e6e2b5f03e"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win32.whl", hash = "sha256:f187a9c3b940ce1ee324710626daf72c05599946bd6748abe9e289f1daa9a077"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8e9130fe5d7c9182990b366ad78fd632f744097e753e08ace573877d67c32f8"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win_arm64.whl", hash = "sha256:40419e98b10cd6a00ce26e4837a67362f658fc3cd7a71bd8bd25c99f7ee8fea5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b5d5072b548db1b313a07d62d88fe0b037bd2783c16607c647e01b070f6cf9e5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf5bcf22e1f0fd273354462631d443ef78d677f7d2fc292de2aec72ae1473e66"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c8fc973adde8ed52810f590410e03fb6f0b541bbaeb04c38d77e63442b2df4c"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2464bb120f135293e9a712e342c43695d3d83168907df05f8c4ead1612310c7"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d9d58689aca22057cf1a5851677b8a3ccc9b535ca008c7ed06dc6e1899f7844"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167e745f98baa0f3034c13583e6302fb69249a01239f1483d68c27abb841e0a1"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0bf0663b4b6da1507869722420ea9356b6195aa907228d6201303e69837af9"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd6ac61b74fdb9e23f04d5f068e6cf554f47e77228ca28aa2347a6ca8903972f"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:60ff67c690acecf381759c16cb06c878328fe2361ddf77b25d0e434ea48a29da"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:cb934363380c60f3a57d14af94325125cd8cded9822611a9f78220444034e36e"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fe833493fb5cc5682c823ea3e2f7066b07612ee8f61ecdf03e1268f262106cdd"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2797fb847d89e04040d281cb1902cbeffbc4b5131a5c53fc0db490fd76b2a547"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win32.whl", hash = "sha256:52e3d89377744dae68ed7c84ad0ddd3f5e891c82d48d26423b9e066fc835cc7c"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:c76da20481c906e08400ee9be230f9e611d5931a33707d9df40337c2655c84b5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win_arm64.whl", hash = "sha256:f2d2846f3980445864c7e8b8818a29707fcaff2f0261159ef6b7bd27ba139296"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355fc4a268ffa07bab88d9adee173783ec8d20136059e028d2a9135c623c44e6"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4d81a78f90269190b568a8353d4ea86015289c36d7e525cd4d43176c88eff429"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e618625ffc4660b26dc8e56225f8b966d5842fa190e70c60db6cd393e25b86e"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b712336ad6f2bacdbc9f1452556e8942269ef71f60a9e6883ef1726b52d9228a"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc1ee19fdad05770c897e793836c002344524301501d71ef2e832847425707"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1950f8597890c0c707cb7e0416c62a1cf03dcdb0384bc0b2dbda7e05efe738ec"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a6c35f272ec9c430568dc8c1c30cb873f6bc96be2c79795e0bce6db4e0e101d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1df0f9e9239132a231c86ae4f545ec2b55409fa44470692fcfb36b1bd00157ad"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d2c51955329bfccf99ae26f63d5928bf5be9fcfcd9f458f6847fd4b7e2b8986c"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:3c522f462d9fc504f2ea8d82e44aa580e60566acc754422c829ad75c752fbf8d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:d8a52fc50ded60d81117d7647f262c529659fb21d23e14ebfd0b35efa4f1b83d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:04dbdfb0f0bfd3f99cf1e9e24fadc6ded2736d7933f32f1151b0f2abb38f9a25"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-win32.whl", hash = "sha256:4968c8bd1df84b42f382549e6226710ad3476f976389839168db3e68fd373298"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:3fe4545f89f8d6c27b6bbbabfe40839624873c08bd6700f63ac36970a179f8f5"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f256c8fb8f3125574c8c0c919ab0a1f75d7cba4d053dda2e762dcc36357969d"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fdc09cf6e9d8eac3ce48a4615b3a3ee332ea84ac9657dbbefef913b13e632f"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d395d46b80063d3b5d13c0af43d2c2cedf3ab48c6a0c2aeec715aa5455b0c632"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa714fb96ce9e70c37e64c83b62fe8307030081a0bfae74a76fac7ba0f91715"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc1a0f29f9119be7a8d3c720f1d2068317ae532e39e4f7f948607c3a6de8396"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6022674aa1747d6300f699cd7c54d7dae89bfe1f84556de699c4ac5df0838082"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb72e5f9762fd469701a7e12e94b924af9004954f8c739f925cb19c00862e38"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad04ae301129f0eb5b350a333accd375ce155a0c1cec85ab0ec01f770214e2e4"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f46a22506f17c0433e349f2d1dc11907c393d9b3601b91d4e334fa9a439a6a4d"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:01b42a8728c36011718da409aa86b84984396bf0ca3bfb6e62624f2014f6022c"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e590d5d5443cf56f83a51d3c4867bd1f6be8ef8cfcc44279522bcef3845b2a51"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4c72078b5fdce34ba5753f9299ae304e282420e6455e043ad08e4488ca13a2b0"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win32.whl", hash = "sha256:f75639277304e9b75e6a7b3c07042d2264e16740a11e449645689ed28e9c2124"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:e81e27e8c32a1e1278a4bb1ce31401bfaa8c2cc697a053b985a6f8d013df83ec"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win_arm64.whl", hash = "sha256:15bc397ee9a3ed1210b629b9f5f1da809244adc51ce620c504138c6e7095b7bd"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:20488ade4e1ddba3cfad04f400da7a9c1b91eff5b7bd3d1c50b385d78b587f4f"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:e61b03509b1a6eb31bc5582694f6df837d340535da7eba7bedb8ae42a2fcd0b9"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:098d231d4e51644d421a641f4a5f2f151f856f53c252b03516e01389b2bfef99"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17ab8b7d10fde8dd763ad428aa961c0f30a1b44426e675186af8903b5d134fb0"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e272df61bee0a056a3daf99f9b1bd82cf73ace7d668894788139c868fdf37d6f"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d6481e099ff8c4edda85b8b9b5174c200540fd23c8f38120016c765a86fa01f5"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad61676e9bdae677d577fe80ec1c2cea1d150c86be647e652551dcfe505b1113"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:af65020c0dd48d0d8ae405e7e69b9d8ae306eb9b6249ca8bf511a13f465fad85"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d38b4e026fcd580e0bda6c0ae941e0e9a52c6bc66cdce0b8b0da61e1959f5f8"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f74ed072c2b9dc6743fb19994319d443a4330b0e64aeba0aa9105406c7c5b9c2"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee5f6b8321f90615c184bd8a4c676e9becda69b8e4e451a90923db719d6857c"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3a555e3c841d6efa350f862204bb0a3fea0c006b8acc9b152b374fa36518a1c6"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0772150d37bf018110351c01d032bf9ab25127b966a29830faa8ad69b7e2f651"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:addcdd3c3deef1bd54075bd7aba0a6ea9f1d01764a08620074b7a7b1e5447cb9"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fe86b82b776554add8f900b6af202b74eb5efe8f25acdb8680a5c977608727f"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0fc91ac59f4414d8542454dfd6287a154b8e6f1256718c898f695bdbb993467"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a944e546a296a5fdcaabb537b01459f1b14d66f74e584cb2a91448bffadc3c1"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fb96ba96d58c668a17a06b5b5e8340fedc26188e87b0d229d38104556f30cd8"}, + {file = "rapidfuzz-3.9.4.tar.gz", hash = "sha256:366bf8947b84e37f2f4cf31aaf5f37c39f620d8c0eddb8b633e6ba0129ca4a0a"}, +] + +[package.extras] +full = ["numpy"] + +[[package]] +name = "redis" +version = "5.0.7" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, + {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.31" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"}, + {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"}, + {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "stackprinter" +version = "0.2.12" +description = "Debug-friendly stack traces, with variable values and semantic highlighting" +optional = false +python-versions = ">=3.4" +files = [ + {file = "stackprinter-0.2.12-py3-none-any.whl", hash = "sha256:0a0623d46a5babd7a8a9787f605f4dd4a42d6ff7aee140541d5e9291a506e8d9"}, + {file = "stackprinter-0.2.12.tar.gz", hash = "sha256:271efc75ebdcc1554e58168ea7779f98066d54a325f57c7dc19f10fa998ef01e"}, +] + +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.30.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "0.22.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "ee9c95e5603a23661a82e8d4df62fce700e7249e0cd145453c22c44625cf3359" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..62e2dff --- /dev/null +++ b/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +create = true diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ad57b1a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[tool.poetry] +name = "stream-fusion" +version = "v0.1.0" +description = "StreamFusion is an advanced plugin for Stremio that significantly enhances its streaming capabilities with debrid service." +authors = ["LimeDrive "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +fastapi = "^0.111.0" +uvicorn = "^0.30.1" +starlette = "^0.37.2" +requests = "^2.32.3" +bencode-py = "^4.0.0" +jinja2 = "^3.1.4" +aiocron = "^1.8" +python-dotenv = "^1.0.1" +rank-torrent-name = "^0.2.21" +cachetools = "^5.3.3" +redis = "^5.0.7" +aiohttp = "^3.9.5" +gunicorn = "^22.0.0" +loguru = "^0.7.2" +stackprinter = "^0.2.12" +pydantic-settings = "^2.3.4" +sqlalchemy = "^2.0.31" +aiosqlite = "^0.20.0" +toml = "^0.10.2" +jsonpickle = "^3.2.2" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/stream_fusion/__init__.py b/stream_fusion/__init__.py new file mode 100644 index 0000000..7bf6164 --- /dev/null +++ b/stream_fusion/__init__.py @@ -0,0 +1 @@ +"""stream_fusion package.""" diff --git a/stream_fusion/__main__.py b/stream_fusion/__main__.py new file mode 100644 index 0000000..d375e45 --- /dev/null +++ b/stream_fusion/__main__.py @@ -0,0 +1,36 @@ +import uvicorn + +from stream_fusion.gunicorn_runner import GunicornApplication +from stream_fusion.settings import settings + + +def main() -> None: + """Entrypoint of the application.""" + if settings.reload: + uvicorn.run( + "stream_fusion.web.application:get_app", + workers=settings.workers_count, + host=settings.host, + port=settings.port, + reload=settings.reload, + log_level=settings.log_level.value.lower(), + factory=True, + ) + else: + # We choose gunicorn only if reload + # option is not used, because reload + # feature doen't work with GUvicorn workers. + GunicornApplication( + "stream_fusion.web.application:get_app", + host=settings.host, + port=settings.port, + workers=settings.workers_count, + factory=True, + accesslog="-", + loglevel=settings.log_level.value.lower(), + access_log_format='%r "-" %s "-" %Tf', + ).run() + + +if __name__ == "__main__": + main() diff --git a/stream_fusion/constants.py b/stream_fusion/constants.py new file mode 100644 index 0000000..7ff0150 --- /dev/null +++ b/stream_fusion/constants.py @@ -0,0 +1,87 @@ +NO_CONFIG = {'streams': [{'url': "#", 'title': "No configuration found"}]} +JACKETT_ERROR = {'streams': [{'url': "#", 'title': "An error occured"}]} + +CACHER_URL = "https://stremio-jackett-cacher.elfhosted.com/" + +DMM_API_URL = "http://135.181.110.62:8181" + +NO_CACHE_VIDEO_URL = "https://github.com/aymene69/stremio-jackett/raw/main/source/videos/nocache.mp4" + +EXCLUDED_TRACKERS = ['0day.kiev', '1ptbar', '2 Fast 4 You', '2xFree', '3ChangTrai', '3D Torrents', '3Wmg', '4thD', + '52PT', '720pier', 'Abnormal', 'ABtorrents', 'Acid-Lounge', 'Across The Tasman', 'Aftershock', + 'AGSVPT', 'Aidoru!Online', 'Aither (API)', 'AlphaRatio', 'Amigos Share Club', 'AniDUB', + 'Anime-Free', 'AnimeBytes', 'AnimeLayer', 'AnimeTorrents', 'AnimeTorrents.ro', 'AnimeWorld (API)', + 'AniToons', 'Anthelion (API)', 'ArabaFenice', 'ArabP2P', 'ArabTorrents', 'ArenaBG', 'AsianCinema', + 'AsianDVDClub', 'Audiences', 'AudioNews', 'Aussierul.es', 'AvistaZ', 'Azusa', 'Back-ups', 'BakaBT', + 'BeiTai', 'Beload', 'Best-Core', 'Beyond-HD (API)', 'Bibliotik', 'Bit-Bázis', 'BIT-HDTV', 'Bitded', + 'Bithorlo', 'BitHUmen', 'BitPorn', 'Bitspyder', 'Bittorrentfiles', 'BiTTuRK', 'BJ-Share', + 'BlueBird', 'Blutopia (API)', 'BookTracker', 'BootyTape', 'Borgzelle', 'Boxing Torrents', + 'BrasilTracker', 'BroadcasTheNet', 'BroadCity', 'BrokenStones', 'BrSociety (API)', 'BTArg', + 'BTNext', 'BTSCHOOL', 'BwTorrents', 'BYRBT', 'Carp-Hunter', 'Carpathians', 'CarPT', 'CartoonChaos', + 'Cathode-Ray.Tube', 'Catorrent', 'Central Torrent', 'CeskeForum', 'CGPeers', 'CHDBits', 'cheggit', + 'ChileBT', 'Cinemageddon', 'CinemaMovieS_ZT', 'Cinematik', 'CinemaZ', 'Classix', 'Coastal-Crew', + 'Concertos', 'CrazySpirits', 'CrnaBerza', 'CRT2FA', 'Dajiao', 'DanishBytes (API)', 'Dark-Shadow', + 'DataScene (API)', 'Deildu', 'Demonoid', 'DesiTorrents (API)', 'Devil-Torrents', 'Diablo Torrent', + 'DICMusic', 'DigitalCore', 'DimeADozen', 'DiscFan', 'DivTeam', 'DocsPedia', 'Dream Tracker', + 'DreamingTree', 'Drugari', 'DXP', 'Ebooks-Shares', 'Electro-Torrent', 'Empornium', 'Empornium2FA', + 'EniaHD', 'Enthralled', 'Enthralled2FA', 'Erai-Raws', 'eShareNet', 'eStone', 'Ex-torrenty', + 'exitorrent.org', 'ExKinoRay', 'ExoticaZ', 'ExtremeBits', 'ExtremlymTorrents', + 'Falkon Vision Team', 'FANO.IN', 'Fantastiko', 'Fappaizuri', 'FastScene', 'FearNoPeer', + 'Femdomcult', 'File-Tracker', 'FileList', 'FinElite', 'FinVip', 'Flux-Zone', 'Free Farm', 'FSM', + 'FunFile', 'FunkyTorrents', 'FutureTorrent', 'Fuzer', 'Gamera', 'Gay-Torrents.net', + 'gay-torrents.org', 'GAYtorrent.ru', 'GazelleGames', 'GazelleGames (API)', 'Generation-Free (API)', + 'Genesis-Movement', 'GigaTorrents', 'GimmePeers', 'Girotorrent', 'GreatPosterWall', 'GreekDiamond', + 'HaiDan', 'Haitang', 'HappyFappy', 'Hares Club', 'hawke-uno', 'HD-CLUB', 'HD-CzTorrent', + 'HD-Forever', 'HD-Olimpo (API)', 'HD-Only', 'HD-Space', 'HD-Torrents', 'HD-UNiT3D (API)', + 'HD4FANS', 'HDArea', 'HDAtmos', 'HDBits (API)', 'HDC', 'HDDolby', 'HDFans', 'HDFun', 'HDGalaKtik', + 'HDHome', 'HDMaYi', 'HDPT', 'HDRoute', 'HDSky', 'HDtime', 'HDTorrents.it', 'HDTurk', 'HDU', + 'hdvbits', 'HDVIDEO', 'Hebits', 'HellasHut', 'HellTorrents', 'HHanClub', 'HomePornTorrents', + 'House of Devil', 'HQMusic', 'HunTorrent', 'iAnon', 'ICC2022', 'Il Corsaro Blu', 'ilDraGoNeRo', + 'ImmortalSeed', 'Immortuos', 'Indietorrents', 'Infire', 'Insane Tracker', 'IPTorrents', + 'ItaTorrents', 'JME-REUNIT3D (API)', 'JoyHD', 'JPopsuki', 'JPTV (API)', 'KamePT', 'Karagarga', + 'Keep Friends', 'KIMOJI', 'Kinorun', 'Kinozal', 'Kinozal (M)', 'Korsar', 'KrazyZone', 'Kufei', + 'Kufirc', 'LaidBackManor (API)', 'Last Digital Underground', 'LastFiles', 'Lat-Team (API)', + 'Le-Cinephile', 'LearnBits', 'LearnFlakes', 'leech24', 'Les-Cinephiles', 'LeSaloon', 'Lesbians4u', + 'Libble', 'LibraNet', 'LinkoManija', 'Locadora', 'LosslessClub', 'LostFilm.tv', 'LST', + 'M-Team - TP', 'M-Team - TP (2FA)', 'MaDs Revolution', 'Magnetico (Local DHT)', 'Majomparádé', + 'Making Off', 'Marine Tracker', 'Masters-TB', 'Mazepa', 'MDAN', 'MegamixTracker', + 'Mendigos da WEB', 'MeseVilág', 'Metal Tracker', 'MetalGuru', 'Milkie', 'MIRCrew', 'MMA-torrents', + 'MNV', 'MOJBLiNK', 'MonikaDesign (API)', 'MoreThanTV (API)', 'MouseBits', 'Movie-Torrentz', + 'MovieWorld', 'MuseBootlegs', 'MVGroup Forum', 'MVGroup Main', 'MyAnonamouse', 'MySpleen', 'nCore', + 'NebulanceAPI', 'NetHD', 'NewStudioL', 'NicePT', 'NoNaMe ClubL', 'NorBits', 'NORDiCHD', + 'Ntelogo (API)', 'OKPT', 'Old Toons World', 'OnlyEncodes (API)', 'OpenCD', 'Orpheus', 'OshenPT', + 'Ostwiki', 'OurBits', 'P2PBG', 'Panda', 'Party-Tracker', 'PassThePopcorn', 'Peeratiko', 'Peers.FM', + 'PigNetwork', 'PixelCove', 'PixelCove2FA', 'PiXELHD', 'PolishSource', 'PolishTracker (API)', + 'Pornbay', 'PornoLab', 'Portugas (API)', 'PotUK', 'PreToMe', 'PrivateHD', 'ProAudioTorrents', + 'PTCafe', 'PTChina', 'PTerClub', 'PTFiles', 'PThome', 'PTLSP', 'PTSBAO', 'PTTime', 'PT分享站', + "Punk's Horror Tracker", 'PuntoTorrent', 'PussyTorrents', 'PuTao', 'PWTorrents', 'R3V WTF!', + 'Racing4Everyone (API)', 'RacingForMe', 'Rainbow Tracker', 'RareShare2 (API)', 'Red Leaves', + 'Red Star Torrent', 'Redacted', 'RedBits (API)', 'ReelFLiX (API)', 'Resurrect The Net', + 'RetroFlix', 'RevolutionTT', 'RGFootball', 'RinTor', 'RiperAM', 'RM-HD', 'RockBox', + 'Romanian Metal Torrents', 'Rousi', 'RPTScene', 'RUDUB', 'Rustorka', 'RuTracker', 'SATClubbing', + 'SceneHD', 'SceneLinks', 'SceneRush', 'SceneTime', 'Secret Cinema', 'SeedFile', 'seleZen', + 'Shadowflow', 'Shareisland (API)', 'Sharewood', 'Sharewood API', 'SharkPT', 'Shazbat', 'SiamBIT', + 'SkipTheCommercials (API)', 'SkipTheTrailers', 'SkTorrent', 'SkTorrent-org', 'slosoul', 'SnowPT', + 'SoulVoice', 'Speed.cd', 'SpeedApp', 'Speedmaster HD', 'SpeedTorrent Reloaded', + 'Spirit of Revolution', 'SportsCult', 'SpringSunday', 'SugoiMusic', 'Superbits', 'Swarmazon (API)', + 'Tapochek', 'Tasmanit', 'Team CT Game', 'TeamHD', 'TeamOS', 'TEKNO3D', 'teracod', 'The Crazy Ones', + 'The Empire', 'The Falling Angels', 'The Geeks', 'The New Retro', 'The Occult', + 'The Old School (API)', 'The Place', 'The Shinning (API)', 'The Show', 'The Vault', 'The-New-Fun', + 'TheLeachZone', 'themixingbowl', 'TheRebels (API)', 'TheScenePlace', "Thor's Land", 'TJUPT', + 'TLFBits', 'TmGHuB', 'Toca Share', 'Toloka.to', 'ToonsForMe', 'Tornado', 'Torrent Heaven', + 'Torrent Network', 'Torrent Sector Crew', 'Torrent Trader', 'Torrent-Explosiv', 'Torrent-Syndikat', + 'TOrrent-tuRK', 'Torrent.LT', 'TorrentBD', 'TorrentBytes', 'TorrentCCF', 'TorrentDay', 'TorrentDD', + 'Torrenteros (API)', 'TorrentHeaven', 'TorrentHR', 'Torrenting', 'Torrentland', + 'Torrentland (API)', 'TorrentLeech', 'Torrentleech.pl', 'TorrentMasters', 'Torrents-Local', + 'TorrentSeeds (API)', 'TotallyKids', 'ToTheGlory', 'ToTheGloryCookie', 'TrackerMK', + 'TranceTraffic', 'Trellas', 'TreZzoR', 'TreZzoRCookie', 'TribalMixes', 'TurkTorrent', 'TV Store', + 'TVChaosUK', 'TvRoad', 'Twisted-Music', 'U2', 'UBits', 'UHDBits', 'UltraHD', 'Union Fansub', + 'UnionGang', 'UniOtaku', 'Universal-Torrents', 'Unlimitz', 'upload.cx', 'UTOPIA', 'WDT', + 'White Angel', 'WinterSakura', 'World-In-HD', 'World-of-Tomorrow', 'Wukong', 'x-ite.me', + 'XbytesV2', 'Xider-Torrent', 'XSpeeds', 'Xthor (API)', 'xTorrenty', 'Xtreme Bytes', 'XWT-Classics', + 'XWtorrents', 'YDYPT', 'YGGcookie', 'YGGtorrent', 'Zamunda.net', 'Zelka.org', 'ZmPT (织梦)', + 'ZOMB', 'ZonaQ', 'Ztracker'] + +REDIS_HOST = 'redis' + +REDIS_PORT = 6379 \ No newline at end of file diff --git a/stream_fusion/gunicorn_runner.py b/stream_fusion/gunicorn_runner.py new file mode 100644 index 0000000..52b9348 --- /dev/null +++ b/stream_fusion/gunicorn_runner.py @@ -0,0 +1,86 @@ +import os + +from typing import Any + +from gunicorn.app.base import BaseApplication +from gunicorn.util import import_app +from uvicorn.workers import UvicornWorker as BaseUvicornWorker +from stream_fusion.logging_config import configure_logging + +try: + import uvloop # (Found nested import) +except ImportError: + uvloop = None # type: ignore # (variables overlap) + + +class UvicornWorker(BaseUvicornWorker): + """ + Configuration for uvicorn workers. + + This class is subclassing UvicornWorker and defines + some parameters class-wide, because it's impossible, + to pass these parameters through gunicorn. + """ + + CONFIG_KWARGS = { # (upper-case constant in a class) # noqa: RUF012 + "loop": "uvloop" if uvloop is not None else "asyncio", + "http": "httptools", + "lifespan": "on", + "factory": True, + "proxy_headers": False, + } + + +class GunicornApplication(BaseApplication): + """ + Custom gunicorn application. + + This class is used to start guncicorn + with custom uvicorn workers. + """ + + def __init__(self, app: str, host: str, port: int, workers: int, **kwargs: Any): + self.options = { + "bind": f"{host}:{port}", + "workers": workers, + "worker_class": "stream_fusion.gunicorn_runner.UvicornWorker", + "logconfig_dict": { + 'version': 1, + 'disable_existing_loggers': False, + }, + **kwargs, + } + self.app = app + super().__init__() + + def load(self) -> str: + os.environ["RUNNING_UNDER_GUNICORN"] = "1" + return import_app(self.app) + + def when_ready(self, server): + configure_logging() + + def load_config(self) -> None: + """ + Load config for web server. + + This function is used to set parameters to gunicorn + main process. It only sets parameters that + gunicorn can handle. If you pass unknown + parameter to it, it crash with error. + """ + for key, value in self.options.items(): + if key in self.cfg.settings and value is not None: + self.cfg.set(key.lower(), value) + + def load(self) -> str: + """ + Load actual application. + + Gunicorn loads application based on this + function's returns. We return python's path to + the app's factory. + + :returns: python path to app factory. + """ + return import_app(self.app) diff --git a/stream_fusion/logging_config.py b/stream_fusion/logging_config.py new file mode 100644 index 0000000..966b043 --- /dev/null +++ b/stream_fusion/logging_config.py @@ -0,0 +1,109 @@ +# logging_config.py +from loguru import logger +from stream_fusion.settings import settings +import sys +import logging +import inspect +import re +import stackprinter + +REDACTED = settings.log_redacted +patterns = [ + r"/ey.*?/", +] + +class SecretFilter: + def __init__(self, patterns): + self._patterns = patterns + + def __call__(self, record): + record["message"] = self.redact(record["message"]) + if "stack" in record["extra"]: + record["extra"]["stack"] = self.redact(record["extra"]["stack"]) + return record + + def redact(self, message): + for pattern in self._patterns: + message = re.sub(pattern, "****", message) + return message + + +def format(record): + format_ = "{time} {level} {function} {message}\n" + pats = [ + r"/ey.*?/", + ] + if record["exception"] is not None: + stack = stackprinter.format( + record["exception"], + suppressed_vars=[ + r".*ygg_playload.*", + ], + ) + if REDACTED: + for pat in pats: + stack = re.sub(pat, "/****/", stack) + record["extra"]["stack"] = stack + format_ += "{extra[stack]}\n" + return format_ + +class InterceptHandler(logging.Handler): + def emit(self, record: logging.LogRecord) -> None: + level: str | int + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + frame, depth = inspect.currentframe(), 0 + while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__): + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log( + level, record.getMessage() + ) + +def is_running_under_gunicorn(): + return "gunicorn" in sys.modules + +def configure_logging(): + + intercept_handler = InterceptHandler() + + logging.basicConfig(handlers=[intercept_handler], level=logging.NOTSET) + + if is_running_under_gunicorn(): + # Configuration spécifique pour Gunicorn + gunicorn_logger = logging.getLogger('gunicorn.error') + logger.remove() + logger.add(gunicorn_logger.handlers[0], format=format, level=gunicorn_logger.level) + + for logger_name in logging.root.manager.loggerDict: + if logger_name.startswith("uvicorn."): + logging.getLogger(logger_name).handlers = [] + + # change handler for default uvicorn logger + logging.getLogger("uvicorn").handlers = [intercept_handler] + logging.getLogger("uvicorn.access").handlers = [intercept_handler] + + logger.remove() + + logger.add( + sys.stdout, + format=format, + level=settings.log_level.value, + colorize=True, + filter=SecretFilter(patterns) if REDACTED else None, + ) + + logger.add( + settings.log_path, + format=format, + level="DEBUG", + rotation="2 MB", + retention="5 days", + compression="zip", + enqueue=True, + filter=SecretFilter(patterns) if REDACTED else None, + ) diff --git a/stream_fusion/services/redis/redis_config.py b/stream_fusion/services/redis/redis_config.py new file mode 100644 index 0000000..9174a14 --- /dev/null +++ b/stream_fusion/services/redis/redis_config.py @@ -0,0 +1,26 @@ +from functools import lru_cache +from fastapi import Depends +from stream_fusion.settings import settings +from stream_fusion.utils.cache.local_redis import RedisCache + +@lru_cache() +def get_redis_config(): + return { + "redisHost": settings.redis_host, + "redisPort": settings.redis_port, + "redisExpiration": settings.redis_expiration, + } + +@lru_cache() +def create_redis_cache(): + return RedisCache(get_redis_config()) + +def get_redis_cache(): + return create_redis_cache() + +async def get_redis_cache_dependency(): + redis_cache = get_redis_cache() + try: + yield redis_cache + finally: + redis_cache.close() \ No newline at end of file diff --git a/stream_fusion/services/security_db/__init__.py b/stream_fusion/services/security_db/__init__.py new file mode 100644 index 0000000..e9229e6 --- /dev/null +++ b/stream_fusion/services/security_db/__init__.py @@ -0,0 +1,4 @@ +"""Security Database Service""" +from stream_fusion.services.security_db._sqlite_access import security_db_access + +__all__ = ["security_db_access"] diff --git a/stream_fusion/services/security_db/_sqlite_access.py b/stream_fusion/services/security_db/_sqlite_access.py new file mode 100644 index 0000000..3254b05 --- /dev/null +++ b/stream_fusion/services/security_db/_sqlite_access.py @@ -0,0 +1,181 @@ +"""Interaction with SQLite database.""" + +import sqlite3 +import threading +import uuid +from datetime import datetime, timedelta +from typing import List, Optional, Tuple + +from fastapi import HTTPException +from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY + +from stream_fusion.logging_config import logger +from stream_fusion.settings import settings + + +class SQLiteAccess: + """Class handling SQLite connection and operations.""" + + def __init__(self): + self.db_location = settings.db_path + self.expiration_limit = getattr(settings, "security_api_key_expiration", 15) + if self.expiration_limit == 15: + logger.warning( + "Security API key expiration not found or invalid in settings. Using default value of 15 days." + ) + self.init_db() + + def init_db(self): + """Initialize the database and perform necessary migrations.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS stream_fusion_security ( + api_key TEXT PRIMARY KEY, + is_active INTEGER, + never_expire INTEGER, + expiration_date TEXT, + latest_query_date TEXT, + total_queries INTEGER, + name TEXT + ) + """ + ) + conn.commit() + + def create_key(self, name: str, never_expire: bool) -> str: + """Create a new API key.""" + api_key = str(uuid.uuid4()) + expiration_date = ( + (datetime.utcnow() + timedelta(days=self.expiration_limit)).isoformat( + timespec="seconds" + ) + if not never_expire + else None + ) + + with sqlite3.connect(self.db_location) as conn: + conn.execute( + """ + INSERT INTO stream_fusion_security ( + api_key, + is_active, + never_expire, + expiration_date, + latest_query_date, + total_queries, name) VALUES (?, 1, ?, ?, NULL, 0, ?) + """, + (api_key, int(never_expire), expiration_date, name), + ) + return api_key + + def renew_key(self, api_key: str, new_expiration_date: Optional[str] = None) -> str: + """Renew an existing API key.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT is_active, never_expire FROM stream_fusion_security WHERE api_key = ?", + (api_key,), + ) + result = cursor.fetchone() + + if not result: + raise HTTPException( + status_code=HTTP_404_NOT_FOUND, detail="API key not found" + ) + + is_active, never_expire = result + response_lines = [] + + if not is_active: + response_lines.append( + "This API key was revoked and has been reactivated." + ) + + if not never_expire: + if not new_expiration_date: + new_expiration_date = ( + datetime.utcnow() + timedelta(days=self.expiration_limit) + ).isoformat(timespec="seconds") + else: + try: + new_expiration_date = datetime.fromisoformat( + new_expiration_date + ).isoformat(timespec="seconds") + except ValueError: + raise HTTPException( + status_code=HTTP_422_UNPROCESSABLE_ENTITY, + detail="The expiration date could not be parsed. Please use ISO 8601 format.", + ) + + cursor.execute( + "UPDATE stream_fusion_security SET expiration_date = ?, is_active = 1 WHERE api_key = ?", + (new_expiration_date, api_key), + ) + response_lines.append( + f"The new expiration date for the API key is {new_expiration_date}" + ) + + return " ".join(response_lines) + + def revoke_key(self, api_key: str): + """Revoke an API key.""" + with sqlite3.connect(self.db_location) as conn: + conn.execute( + "UPDATE stream_fusion_security SET is_active = 0 WHERE api_key = ?", + (api_key,), + ) + + def check_key(self, api_key: str) -> bool: + """Check if an API key is valid.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT is_active, total_queries, expiration_date, never_expire FROM stream_fusion_security WHERE api_key = ?", + (api_key,), + ) + result = cursor.fetchone() + + if ( + not result + or not result[0] + or ( + not result[3] + and datetime.fromisoformat(result[2]) < datetime.utcnow() + ) + ): + return False + + threading.Thread( + target=self._update_usage, args=(api_key, result[1]) + ).start() + return True + + def _update_usage(self, api_key: str, usage_count: int): + """Update usage statistics for an API key.""" + with sqlite3.connect(self.db_location) as conn: + conn.execute( + "UPDATE stream_fusion_security SET total_queries = ?, latest_query_date = ? WHERE api_key = ?", + ( + usage_count + 1, + datetime.utcnow().isoformat(timespec="seconds"), + api_key, + ), + ) + + def get_usage_stats(self) -> List[Tuple[str, bool, bool, str, str, int, str]]: + """Return usage statistics for all API keys.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + """ + SELECT api_key, is_active, never_expire, expiration_date, latest_query_date, total_queries, name + FROM stream_fusion_security + ORDER BY latest_query_date DESC + """ + ) + return cursor.fetchall() + + +security_db_access = SQLiteAccess() diff --git a/stream_fusion/settings.py b/stream_fusion/settings.py new file mode 100644 index 0000000..0a9a645 --- /dev/null +++ b/stream_fusion/settings.py @@ -0,0 +1,70 @@ +import enum +from pydantic import ValidationError +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class LogLevel(str, enum.Enum): + """Possible log levels.""" + + NOTSET = "NOTSET" + DEBUG = "DEBUG" + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" + FATAL = "FATAL" + + +class Settings(BaseSettings): + """Settings for the application""" + + # STREAM-FUSION + workers_count: int = 4 + port: int = 8080 + host: str = "0.0.0.0" + timeout: int = 180 + reload: bool = True + # LOGGING + log_level: LogLevel = LogLevel.INFO + log_path: str = "/app/config/logs/stream-fusion.log" + log_redacted: bool = True + # SECUIRITY + secret_api_key: str = "superkey_that_can_be_changed" + security_hide_docs: bool = True + # SQLITE + db_path: str = "/app/config/stream-fusion.db" + db_echo: bool = False + db_timeout: int = 15 + # REDIS + redis_host: str = "redis" + redis_port: int = 6379 + redis_expiration: int = 604800 + # TMDB + tmdb_api_key: str = "tmdb_api_key" + # FLARESOLVERR + flaresolverr_host: str = "localhost" + flaresolverr_shema: str = "http" + flaresolverr_port: int = 8191 + # JACKETT + jackett_host: str = "localhost" + jackett_shema: str = "http" + jackett_port: int = 9117 + jackett_api_key: str = "jackett_api_key" + # ZILEAN DMM API + zilean_dmm_api_key: str = "zilean_dmm_api_key" # TODO: check to protéct Zilane API with APIKEY + zilean_url: str = "https://zilean.io/api/v1/dmm/search" + # DEVELOPMENT + debug: bool = True + dev_host: str = "0.0.0.0" + dev_port: int = 8080 + # VERSION + version_path: str = "/app/pyproject.toml" + + model_config = SettingsConfigDict( + env_file=".env", secrets_dir="/run/secrets", env_file_encoding="utf-8" + ) + + +try: + settings = Settings() +except ValidationError as e: + raise RuntimeError(f"Configuration validation error: {e}") diff --git a/stream_fusion/static/docs/redoc.standalone.js b/stream_fusion/static/docs/redoc.standalone.js new file mode 100644 index 0000000..6cd53ac --- /dev/null +++ b/stream_fusion/static/docs/redoc.standalone.js @@ -0,0 +1,1804 @@ +/*! For license information please see redoc.standalone.js.LICENSE.txt */!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof define&&define.amd?define(["null"],t):"object"==typeof exports?exports.Redoc=t(require("null")):e.Redoc=t(e.null)}(this,(function(e){return function(){var t={5499:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;let n=r(3325),i=r(6479),o=r(5522),a=r(1603),s=["/properties"],l="http://json-schema.org/draft-07/schema";class c extends n.default{_addVocabularies(){super._addVocabularies(),i.default.forEach((e=>this.addVocabulary(e))),this.opts.discriminator&&this.addKeyword(o.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;let e=this.opts.$data?this.$dataMetaSchema(a,s):a;this.addMetaSchema(e,l,!1),this.refs["http://json-schema.org/schema"]=l}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(l)?l:void 0)}}e.exports=t=c,Object.defineProperty(t,"__esModule",{value:!0}),t.default=c;var u=r(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return u.KeywordCxt}});var p=r(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return p._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return p.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return p.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return p.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return p.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return p.CodeGen}})},4667:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexpCode=t.getProperty=t.safeStringify=t.stringify=t.strConcat=t.addCodeArg=t.str=t._=t.nil=t._Code=t.Name=t.IDENTIFIER=t._CodeOrName=void 0;class r{}t._CodeOrName=r,t.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;class n extends r{constructor(e){if(super(),!t.IDENTIFIER.test(e))throw Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}}t.Name=n;class i extends r{constructor(e){super(),this._items="string"==typeof e?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;let e=this._items[0];return""===e||'""'===e}get str(){var e;return null!==(e=this._str)&&void 0!==e?e:this._str=this._items.reduce(((e,t)=>`${e}${t}`),"")}get names(){var e;return null!==(e=this._names)&&void 0!==e?e:this._names=this._items.reduce(((e,t)=>(t instanceof n&&(e[t.str]=(e[t.str]||0)+1),e)),{})}}function o(e,...t){let r=[e[0]],n=0;for(;n"),GTE:new n._Code(">="),LT:new n._Code("<"),LTE:new n._Code("<="),EQ:new n._Code("==="),NEQ:new n._Code("!=="),NOT:new n._Code("!"),OR:new n._Code("||"),AND:new n._Code("&&"),ADD:new n._Code("+")};class s{optimizeNodes(){return this}optimizeNames(e,t){return this}}class l extends s{constructor(e,t,r){super(),this.varKind=e,this.name=t,this.rhs=r}render({es5:e,_n:t}){let r=e?i.varKinds.var:this.varKind,n=void 0===this.rhs?"":` = ${this.rhs}`;return`${r} ${this.name}${n};`+t}optimizeNames(e,t){if(e[this.name.str])return this.rhs&&(this.rhs=C(this.rhs,e,t)),this}get names(){return this.rhs instanceof n._CodeOrName?this.rhs.names:{}}}class c extends s{constructor(e,t,r){super(),this.lhs=e,this.rhs=t,this.sideEffects=r}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,t){if(!(this.lhs instanceof n.Name)||e[this.lhs.str]||this.sideEffects)return this.rhs=C(this.rhs,e,t),this}get names(){return $(this.lhs instanceof n.Name?{}:{...this.lhs.names},this.rhs)}}class u extends c{constructor(e,t,r,n){super(e,r,n),this.op=t}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}}class p extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}}class d extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}}class f extends s{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}}class h extends s{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,t){return this.code=C(this.code,e,t),this}get names(){return this.code instanceof n._CodeOrName?this.code.names:{}}}class m extends s{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce(((t,r)=>t+r.render(e)),"")}optimizeNodes(){let{nodes:e}=this,t=e.length;for(;t--;){let r=e[t].optimizeNodes();Array.isArray(r)?e.splice(t,1,...r):r?e[t]=r:e.splice(t,1)}return e.length>0?this:void 0}optimizeNames(e,t){let{nodes:r}=this,n=r.length;for(;n--;){let i=r[n];i.optimizeNames(e,t)||(R(e,i.names),r.splice(n,1))}return r.length>0?this:void 0}get names(){return this.nodes.reduce(((e,t)=>A(e,t.names)),{})}}class g extends m{render(e){return"{"+e._n+super.render(e)+"}"+e._n}}class y extends g{}y.kind="else";class v extends g{constructor(e,t){super(t),this.condition=e}render(e){let t=`if(${this.condition})`+super.render(e);return this.else&&(t+="else "+this.else.render(e)),t}optimizeNodes(){super.optimizeNodes();let e=this.condition;if(!0===e)return this.nodes;let t=this.else;if(t){let e=t.optimizeNodes();t=this.else=Array.isArray(e)?new y(e):e}return t?!1===e?t instanceof v?t:t.nodes:this.nodes.length?this:new v(j(e),t instanceof v?[t]:t.nodes):!1!==e&&this.nodes.length?this:void 0}optimizeNames(e,t){var r;if(this.else=null===(r=this.else)||void 0===r?void 0:r.optimizeNames(e,t),super.optimizeNames(e,t)||this.else)return this.condition=C(this.condition,e,t),this}get names(){let e=super.names;return $(e,this.condition),this.else&&A(e,this.else.names),e}}v.kind="if";class b extends g{}b.kind="for";class x extends b{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iteration=C(this.iteration,e,t),this}get names(){return A(super.names,this.iteration.names)}}class w extends b{constructor(e,t,r,n){super(),this.varKind=e,this.name=t,this.from=r,this.to=n}render(e){let t=e.es5?i.varKinds.var:this.varKind,{name:r,from:n,to:o}=this;return`for(${t} ${r}=${n}; ${r}<${o}; ${r}++)`+super.render(e)}get names(){let e=$(super.names,this.from);return $(e,this.to)}}class k extends b{constructor(e,t,r,n){super(),this.loop=e,this.varKind=t,this.name=r,this.iterable=n}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iterable=C(this.iterable,e,t),this}get names(){return A(super.names,this.iterable.names)}}class _ extends g{constructor(e,t,r){super(),this.name=e,this.args=t,this.async=r}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}}_.kind="func";class O extends m{render(e){return"return "+super.render(e)}}O.kind="return";class S extends g{render(e){let t="try"+super.render(e);return this.catch&&(t+=this.catch.render(e)),this.finally&&(t+=this.finally.render(e)),t}optimizeNodes(){var e,t;return super.optimizeNodes(),null===(e=this.catch)||void 0===e||e.optimizeNodes(),null===(t=this.finally)||void 0===t||t.optimizeNodes(),this}optimizeNames(e,t){var r,n;return super.optimizeNames(e,t),null===(r=this.catch)||void 0===r||r.optimizeNames(e,t),null===(n=this.finally)||void 0===n||n.optimizeNames(e,t),this}get names(){let e=super.names;return this.catch&&A(e,this.catch.names),this.finally&&A(e,this.finally.names),e}}class E extends g{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}}E.kind="catch";class P extends g{render(e){return"finally"+super.render(e)}}function A(e,t){for(let r in t)e[r]=(e[r]||0)+(t[r]||0);return e}function $(e,t){return t instanceof n._CodeOrName?A(e,t.names):e}function C(e,t,r){var i;return e instanceof n.Name?o(e):(i=e)instanceof n._Code&&i._items.some((e=>e instanceof n.Name&&1===t[e.str]&&void 0!==r[e.str]))?new n._Code(e._items.reduce(((e,t)=>(t instanceof n.Name&&(t=o(t)),t instanceof n._Code?e.push(...t._items):e.push(t),e)),[])):e;function o(e){let n=r[e.str];return void 0===n||1!==t[e.str]?e:(delete t[e.str],n)}}function R(e,t){for(let r in t)e[r]=(e[r]||0)-(t[r]||0)}function j(e){return"boolean"==typeof e||"number"==typeof e||null===e?!e:n._`!${L(e)}`}P.kind="finally",t.CodeGen=class{constructor(e,t={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...t,_n:t.lines?"\n":""},this._extScope=e,this._scope=new i.Scope({parent:e}),this._nodes=[new class extends m{}]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,t){let r=this._extScope.value(e,t);return(this._values[r.prefix]||(this._values[r.prefix]=new Set)).add(r),r}getScopeValue(e,t){return this._extScope.getValue(e,t)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,t,r,n){let i=this._scope.toName(t);return void 0!==r&&n&&(this._constants[i.str]=r),this._leafNode(new l(e,i,r)),i}const(e,t,r){return this._def(i.varKinds.const,e,t,r)}let(e,t,r){return this._def(i.varKinds.let,e,t,r)}var(e,t,r){return this._def(i.varKinds.var,e,t,r)}assign(e,t,r){return this._leafNode(new c(e,t,r))}add(e,r){return this._leafNode(new u(e,t.operators.ADD,r))}code(e){return"function"==typeof e?e():e!==n.nil&&this._leafNode(new h(e)),this}object(...e){let t=["{"];for(let[r,i]of e)t.length>1&&t.push(","),t.push(r),(r!==i||this.opts.es5)&&(t.push(":"),n.addCodeArg(t,i));return t.push("}"),new n._Code(t)}if(e,t,r){if(this._blockNode(new v(e)),t&&r)this.code(t).else().code(r).endIf();else if(t)this.code(t).endIf();else if(r)throw Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new v(e))}else(){return this._elseNode(new y)}endIf(){return this._endBlockNode(v,y)}_for(e,t){return this._blockNode(e),t&&this.code(t).endFor(),this}for(e,t){return this._for(new x(e),t)}forRange(e,t,r,n,o=(this.opts.es5?i.varKinds.var:i.varKinds.let)){let a=this._scope.toName(e);return this._for(new w(o,a,t,r),(()=>n(a)))}forOf(e,t,r,o=i.varKinds.const){let a=this._scope.toName(e);if(this.opts.es5){let e=t instanceof n.Name?t:this.var("_arr",t);return this.forRange("_i",0,n._`${e}.length`,(t=>{this.var(a,n._`${e}[${t}]`),r(a)}))}return this._for(new k("of",o,a,t),(()=>r(a)))}forIn(e,t,r,o=(this.opts.es5?i.varKinds.var:i.varKinds.const)){if(this.opts.ownProperties)return this.forOf(e,n._`Object.keys(${t})`,r);let a=this._scope.toName(e);return this._for(new k("in",o,a,t),(()=>r(a)))}endFor(){return this._endBlockNode(b)}label(e){return this._leafNode(new p(e))}break(e){return this._leafNode(new d(e))}return(e){let t=new O;if(this._blockNode(t),this.code(e),1!==t.nodes.length)throw Error('CodeGen: "return" should have one node');return this._endBlockNode(O)}try(e,t,r){if(!t&&!r)throw Error('CodeGen: "try" without "catch" and "finally"');let n=new S;if(this._blockNode(n),this.code(e),t){let e=this.name("e");this._currNode=n.catch=new E(e),t(e)}return r&&(this._currNode=n.finally=new P,this.code(r)),this._endBlockNode(E,P)}throw(e){return this._leafNode(new f(e))}block(e,t){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(t),this}endBlock(e){let t=this._blockStarts.pop();if(void 0===t)throw Error("CodeGen: not in self-balancing block");let r=this._nodes.length-t;if(r<0||void 0!==e&&r!==e)throw Error(`CodeGen: wrong number of nodes: ${r} vs ${e} expected`);return this._nodes.length=t,this}func(e,t=n.nil,r,i){return this._blockNode(new _(e,t,r)),i&&this.code(i).endFunc(),this}endFunc(){return this._endBlockNode(_)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,t){let r=this._currNode;if(r instanceof e||t&&r instanceof t)return this._nodes.pop(),this;throw Error(`CodeGen: not in block "${t?`${e.kind}/${t.kind}`:e.kind}"`)}_elseNode(e){let t=this._currNode;if(!(t instanceof v))throw Error('CodeGen: "else" without "if"');return this._currNode=t.else=e,this}get _root(){return this._nodes[0]}get _currNode(){let e=this._nodes;return e[e.length-1]}set _currNode(e){let t=this._nodes;t[t.length-1]=e}},t.not=j;let T=N(t.operators.AND);t.and=function(...e){return e.reduce(T)};let I=N(t.operators.OR);function N(e){return(t,r)=>t===n.nil?r:r===n.nil?t:n._`${L(t)} ${e} ${L(r)}`}function L(e){return e instanceof n.Name?e:n._`(${e})`}t.or=function(...e){return e.reduce(I)}},7791:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.ValueScope=t.ValueScopeName=t.Scope=t.varKinds=t.UsedValueState=void 0;let o=r(4667);class a extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}}(i=n=t.UsedValueState||(t.UsedValueState={}))[i.Started=0]="Started",i[i.Completed=1]="Completed",t.varKinds={const:new o.Name("const"),let:new o.Name("let"),var:new o.Name("var")};class s{constructor({prefixes:e,parent:t}={}){this._names={},this._prefixes=e,this._parent=t}toName(e){return e instanceof o.Name?e:this.name(e)}name(e){return new o.Name(this._newName(e))}_newName(e){return`${e}${(this._names[e]||this._nameGroup(e)).index++}`}_nameGroup(e){var t,r;if((null===(r=null===(t=this._parent)||void 0===t?void 0:t._prefixes)||void 0===r?void 0:r.has(e))||this._prefixes&&!this._prefixes.has(e))throw Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}}t.Scope=s;class l extends o.Name{constructor(e,t){super(t),this.prefix=e}setValue(e,{property:t,itemIndex:r}){this.value=e,this.scopePath=o._`.${new o.Name(t)}[${r}]`}}t.ValueScopeName=l;let c=o._`\n`;t.ValueScope=class extends s{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?c:o.nil}}get(){return this._scope}name(e){return new l(e,this._newName(e))}value(e,t){var r;if(void 0===t.ref)throw Error("CodeGen: ref must be passed in value");let n=this.toName(e),{prefix:i}=n,o=null!==(r=t.key)&&void 0!==r?r:t.ref,a=this._values[i];if(a){let e=a.get(o);if(e)return e}else a=this._values[i]=new Map;a.set(o,n);let s=this._scope[i]||(this._scope[i]=[]),l=s.length;return s[l]=t.ref,n.setValue(t,{property:i,itemIndex:l}),n}getValue(e,t){let r=this._values[e];if(r)return r.get(t)}scopeRefs(e,t=this._values){return this._reduceValues(t,(t=>{if(void 0===t.scopePath)throw Error(`CodeGen: name "${t}" has no value`);return o._`${e}${t.scopePath}`}))}scopeCode(e=this._values,t,r){return this._reduceValues(e,(e=>{if(void 0===e.value)throw Error(`CodeGen: name "${e}" has no value`);return e.value.code}),t,r)}_reduceValues(e,r,i={},s){let l=o.nil;for(let c in e){let u=e[c];if(!u)continue;let p=i[c]=i[c]||new Map;u.forEach((e=>{if(p.has(e))return;p.set(e,n.Started);let i=r(e);if(i){let r=this.opts.es5?t.varKinds.var:t.varKinds.const;l=o._`${l}${r} ${e} = ${i};${this.opts._n}`}else{if(!(i=null==s?void 0:s(e)))throw new a(e);l=o._`${l}${i}${this.opts._n}`}p.set(e,n.Completed)}))}return l}}},1885:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendErrors=t.resetErrorsCount=t.reportExtraError=t.reportError=t.keyword$DataError=t.keywordError=void 0;let n=r(4475),i=r(6124),o=r(5018);function a(e,t){let r=e.const("err",t);e.if(n._`${o.default.vErrors} === null`,(()=>e.assign(o.default.vErrors,n._`[${r}]`)),n._`${o.default.vErrors}.push(${r})`),e.code(n._`${o.default.errors}++`)}function s(e,t){let{gen:r,validateName:i,schemaEnv:o}=e;o.$async?r.throw(n._`new ${e.ValidationError}(${t})`):(r.assign(n._`${i}.errors`,t),r.return(!1))}t.keywordError={message:({keyword:e})=>n.str`should pass "${e}" keyword validation`},t.keyword$DataError={message:({keyword:e,schemaType:t})=>t?n.str`"${e}" keyword must be ${t} ($data)`:n.str`"${e}" keyword is invalid ($data)`},t.reportError=function(e,r=t.keywordError,i,o){let{it:l}=e,{gen:u,compositeRule:p,allErrors:d}=l,f=c(e,r,i);(null!=o?o:p||d)?a(u,f):s(l,n._`[${f}]`)},t.reportExtraError=function(e,r=t.keywordError,n){let{it:i}=e,{gen:l,compositeRule:u,allErrors:p}=i;a(l,c(e,r,n)),u||p||s(i,o.default.vErrors)},t.resetErrorsCount=function(e,t){e.assign(o.default.errors,t),e.if(n._`${o.default.vErrors} !== null`,(()=>e.if(t,(()=>e.assign(n._`${o.default.vErrors}.length`,t)),(()=>e.assign(o.default.vErrors,null)))))},t.extendErrors=function({gen:e,keyword:t,schemaValue:r,data:i,errsCount:a,it:s}){if(void 0===a)throw Error("ajv implementation error");let l=e.name("err");e.forRange("i",a,o.default.errors,(a=>{e.const(l,n._`${o.default.vErrors}[${a}]`),e.if(n._`${l}.instancePath === undefined`,(()=>e.assign(n._`${l}.instancePath`,n.strConcat(o.default.instancePath,s.errorPath)))),e.assign(n._`${l}.schemaPath`,n.str`${s.errSchemaPath}/${t}`),s.opts.verbose&&(e.assign(n._`${l}.schema`,r),e.assign(n._`${l}.data`,i))}))};let l={keyword:new n.Name("keyword"),schemaPath:new n.Name("schemaPath"),params:new n.Name("params"),propertyName:new n.Name("propertyName"),message:new n.Name("message"),schema:new n.Name("schema"),parentSchema:new n.Name("parentSchema")};function c(e,t,r){let{createErrors:a}=e.it;return!1===a?n._`{}`:function(e,t,r={}){let{gen:a,it:s}=e,c=[function({errorPath:e},{instancePath:t}){let r=t?n.str`${e}${i.getErrorPath(t,i.Type.Str)}`:e;return[o.default.instancePath,n.strConcat(o.default.instancePath,r)]}(s,r),function({keyword:e,it:{errSchemaPath:t}},{schemaPath:r,parentSchema:o}){let a=o?t:n.str`${t}/${e}`;return r&&(a=n.str`${a}${i.getErrorPath(r,i.Type.Str)}`),[l.schemaPath,a]}(e,r)];return function(e,{params:t,message:r},i){let{keyword:a,data:s,schemaValue:c,it:u}=e,{opts:p,propertyName:d,topSchemaRef:f,schemaPath:h}=u;i.push([l.keyword,a],[l.params,"function"==typeof t?t(e):t||n._`{}`]),p.messages&&i.push([l.message,"function"==typeof r?r(e):r]),p.verbose&&i.push([l.schema,c],[l.parentSchema,n._`${f}${h}`],[o.default.data,s]),d&&i.push([l.propertyName,d])}(e,t,c),a.object(...c)}(e,t,r)}},7805:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resolveSchema=t.getCompilingSchema=t.resolveRef=t.compileSchema=t.SchemaEnv=void 0;let n=r(4475),i=r(8451),o=r(5018),a=r(9826),s=r(6124),l=r(1321),c=r(540);class u{constructor(e){var t;let r;this.refs={},this.dynamicAnchors={},"object"==typeof e.schema&&(r=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=null!==(t=e.baseId)&&void 0!==t?t:a.normalizeId(null==r?void 0:r[e.schemaId||"$id"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=null==r?void 0:r.$async,this.refs={}}}function p(e){let t=f.call(this,e);if(t)return t;let r,s=a.getFullPath(e.root.baseId),{es5:c,lines:u}=this.opts.code,{ownProperties:p}=this.opts,d=new n.CodeGen(this.scope,{es5:c,lines:u,ownProperties:p});e.$async&&(r=d.scopeValue("Error",{ref:i.default,code:n._`require("ajv/dist/runtime/validation_error").default`}));let h=d.scopeName("validate");e.validateName=h;let m,g={gen:d,allErrors:this.opts.allErrors,data:o.default.data,parentData:o.default.parentData,parentDataProperty:o.default.parentDataProperty,dataNames:[o.default.data],dataPathArr:[n.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:d.scopeValue("schema",!0===this.opts.code.source?{ref:e.schema,code:n.stringify(e.schema)}:{ref:e.schema}),validateName:h,ValidationError:r,schema:e.schema,schemaEnv:e,rootId:s,baseId:e.baseId||s,schemaPath:n.nil,errSchemaPath:e.schemaPath||(this.opts.jtd?"":"#"),errorPath:n._`""`,opts:this.opts,self:this};try{this._compilations.add(e),l.validateFunctionCode(g),d.optimize(this.opts.code.optimize);let t=d.toString();m=`const visitedNodesForRef = new WeakMap(); ${d.scopeRefs(o.default.scope)}return ${t}`,this.opts.code.process&&(m=this.opts.code.process(m,e));let r=Function(`${o.default.self}`,`${o.default.scope}`,m)(this,this.scope.get());if(this.scope.value(h,{ref:r}),r.errors=null,r.schema=e.schema,r.schemaEnv=e,e.$async&&(r.$async=!0),!0===this.opts.code.source&&(r.source={validateName:h,validateCode:t,scopeValues:d._values}),this.opts.unevaluated){let{props:e,items:t}=g;r.evaluated={props:e instanceof n.Name?void 0:e,items:t instanceof n.Name?void 0:t,dynamicProps:e instanceof n.Name,dynamicItems:t instanceof n.Name},r.source&&(r.source.evaluated=n.stringify(r.evaluated))}return e.validate=r,e}catch(t){throw delete e.validate,delete e.validateName,m&&this.logger.error("Error compiling schema, function code:",m),t}finally{this._compilations.delete(e)}}function d(e){return a.inlineRef(e.schema,this.opts.inlineRefs)?e.schema:e.validate?e:p.call(this,e)}function f(e){var t,r;for(let n of this._compilations)if(r=e,(t=n).schema===r.schema&&t.root===r.root&&t.baseId===r.baseId)return n}function h(e,t){let r;for(;"string"==typeof(r=this.refs[t]);)t=r;return r||this.schemas[t]||m.call(this,e,t)}function m(e,t){let r=c.parse(t),n=a._getFullPath(r),i=a.getFullPath(e.baseId);if(Object.keys(e.schema).length>0&&n===i)return y.call(this,r,e);let o=a.normalizeId(n),s=this.refs[o]||this.schemas[o];if("string"==typeof s){let t=m.call(this,e,s);if("object"!=typeof(null==t?void 0:t.schema))return;return y.call(this,r,t)}if("object"==typeof(null==s?void 0:s.schema)){if(s.validate||p.call(this,s),o===a.normalizeId(t)){let{schema:t}=s,{schemaId:r}=this.opts,n=t[r];return n&&(i=a.resolveUrl(i,n)),new u({schema:t,schemaId:r,root:e,baseId:i})}return y.call(this,r,s)}}t.SchemaEnv=u,t.compileSchema=p,t.resolveRef=function(e,t,r){var n;let i=a.resolveUrl(t,r),o=e.refs[i];if(o)return o;let s=h.call(this,e,i);if(void 0===s){let r=null===(n=e.localRefs)||void 0===n?void 0:n[i],{schemaId:o}=this.opts;r&&(s=new u({schema:r,schemaId:o,root:e,baseId:t}))}if(void 0===s&&this.opts.loadSchemaSync){let n=this.opts.loadSchemaSync(t,r,i);!n||this.refs[i]||this.schemas[i]||(this.addSchema(n,i,void 0),s=h.call(this,e,i))}return void 0!==s?e.refs[i]=d.call(this,s):void 0},t.getCompilingSchema=f,t.resolveSchema=m;let g=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function y(e,{baseId:t,schema:r,root:n}){var i;if("/"!==(null===(i=e.fragment)||void 0===i?void 0:i[0]))return;for(let n of e.fragment.slice(1).split("/")){if("boolean"==typeof r||void 0===(r=r[s.unescapeFragment(n)]))return;let e="object"==typeof r&&r[this.opts.schemaId];!g.has(n)&&e&&(t=a.resolveUrl(t,e))}let o;if("boolean"!=typeof r&&r.$ref&&!s.schemaHasRulesButRef(r,this.RULES)){let e=a.resolveUrl(t,r.$ref);o=m.call(this,n,e)}let{schemaId:l}=this.opts;return(o=o||new u({schema:r,schemaId:l,root:n,baseId:t})).schema!==o.root.schema?o:void 0}},5018:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i={data:new n.Name("data"),valCxt:new n.Name("valCxt"),instancePath:new n.Name("instancePath"),parentData:new n.Name("parentData"),parentDataProperty:new n.Name("parentDataProperty"),rootData:new n.Name("rootData"),dynamicAnchors:new n.Name("dynamicAnchors"),vErrors:new n.Name("vErrors"),errors:new n.Name("errors"),this:new n.Name("this"),self:new n.Name("self"),scope:new n.Name("scope"),json:new n.Name("json"),jsonPos:new n.Name("jsonPos"),jsonLen:new n.Name("jsonLen"),jsonPart:new n.Name("jsonPart")};t.default=i},4143:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(9826);t.default=class extends Error{constructor(e,t,r){super(r||`can't resolve reference ${t} from id ${e}`),this.missingRef=n.resolveUrl(e,t),this.missingSchema=n.normalizeId(n.getFullPath(this.missingRef))}}},9826:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSchemaRefs=t.resolveUrl=t.normalizeId=t._getFullPath=t.getFullPath=t.inlineRef=void 0;let n=r(6124),i=r(4063),o=r(4029),a=r(540),s=new Set(["type","format","pattern","maxLength","minLength","maxProperties","minProperties","maxItems","minItems","maximum","minimum","uniqueItems","multipleOf","required","enum","const"]);t.inlineRef=function(e,t=!0){return"boolean"==typeof e||(!0===t?!function e(t){for(let r in t){if(l.has(r))return!0;let n=t[r];if(Array.isArray(n)&&n.some(e)||"object"==typeof n&&e(n))return!0}return!1}(e):!!t&&function e(t){let r=0;for(let i in t)if("$ref"===i||(r++,!s.has(i)&&("object"==typeof t[i]&&n.eachItem(t[i],(t=>r+=e(t))),r===1/0)))return 1/0;return r}(e)<=t)};let l=new Set(["$ref","$recursiveRef","$recursiveAnchor","$dynamicRef","$dynamicAnchor"]);function c(e="",t){return!1!==t&&(e=d(e)),u(a.parse(e))}function u(e){return a.serialize(e).split("#")[0]+"#"}t.getFullPath=c,t._getFullPath=u;let p=/#\/?$/;function d(e){return e?e.replace(p,""):""}t.normalizeId=d,t.resolveUrl=function(e,t){return t=d(t),a.resolve(e,t)};let f=/^[a-z_][-a-z0-9._]*$/i;t.getSchemaRefs=function(e){if("boolean"==typeof e)return{};let{schemaId:t}=this.opts,r=d(e[t]),n={"":r},s=c(r,!1),l={},u=new Set;return o(e,{allKeys:!0},((e,r,i,o)=>{if(void 0===o)return;let c=s+r,m=n[o];function g(t){if(t=d(m?a.resolve(m,t):t),u.has(t))throw h(t);u.add(t);let r=this.refs[t];return"string"==typeof r&&(r=this.refs[r]),"object"==typeof r?p(e,r.schema,t):t!==d(c)&&("#"===t[0]?(p(e,l[t],t),l[t]=e):this.refs[t]=c),t}function y(e){if("string"==typeof e){if(!f.test(e))throw Error(`invalid anchor "${e}"`);g.call(this,`#${e}`)}}"string"==typeof e[t]&&(m=g.call(this,e[t])),y.call(this,e.$anchor),y.call(this,e.$dynamicAnchor),n[r]=m})),l;function p(e,t,r){if(void 0!==t&&!i(e,t))throw h(r)}function h(e){return Error(`reference "${e}" resolves to more than one schema`)}}},3664:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getRules=t.isJSONType=void 0;let r=new Set(["string","number","integer","boolean","null","object","array"]);t.isJSONType=function(e){return"string"==typeof e&&r.has(e)},t.getRules=function(){let e={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...e,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},e.number,e.string,e.array,e.object],post:{rules:[]},all:{},keywords:{}}}},6124:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.checkStrictMode=t.getErrorPath=t.Type=t.useFunc=t.setEvaluated=t.evaluatedPropsToName=t.mergeEvaluated=t.eachItem=t.unescapeJsonPointer=t.escapeJsonPointer=t.escapeFragment=t.unescapeFragment=t.schemaRefOrVal=t.schemaHasRulesButRef=t.schemaHasRules=t.checkUnknownRules=t.alwaysValidSchema=t.toHash=void 0;let o=r(4475),a=r(4667);function s(e,t=e.schema){let{opts:r,self:n}=e;if(!r.strictSchema||"boolean"==typeof t)return;let i=n.RULES.keywords;for(let r in t)i[r]||m(e,`unknown keyword: "${r}"`)}function l(e,t){if("boolean"==typeof e)return!e;for(let r in e)if(t[r])return!0;return!1}function c(e){return"number"==typeof e?`${e}`:e.replace(/~/g,"~0").replace(/\//g,"~1")}function u(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function p({mergeNames:e,mergeToName:t,mergeValues:r,resultToName:n}){return(i,a,s,l)=>{let c=void 0===s?a:s instanceof o.Name?(a instanceof o.Name?e(i,a,s):t(i,a,s),s):a instanceof o.Name?(t(i,s,a),a):r(a,s);return l!==o.Name||c instanceof o.Name?c:n(i,c)}}function d(e,t){if(!0===t)return e.var("props",!0);let r=e.var("props",o._`{}`);return void 0!==t&&f(e,r,t),r}function f(e,t,r){Object.keys(r).forEach((r=>e.assign(o._`${t}${o.getProperty(r)}`,!0)))}t.toHash=function(e){let t={};for(let r of e)t[r]=!0;return t},t.alwaysValidSchema=function(e,t){return"boolean"==typeof t?t:0===Object.keys(t).length||(s(e,t),!l(t,e.self.RULES.all))},t.checkUnknownRules=s,t.schemaHasRules=l,t.schemaHasRulesButRef=function(e,t){if("boolean"==typeof e)return!e;for(let r in e)if("$ref"!==r&&t.all[r])return!0;return!1},t.schemaRefOrVal=function({topSchemaRef:e,schemaPath:t},r,n,i){if(!i){if("number"==typeof r||"boolean"==typeof r)return r;if("string"==typeof r)return o._`${r}`}return o._`${e}${t}${o.getProperty(n)}`},t.unescapeFragment=function(e){return u(decodeURIComponent(e))},t.escapeFragment=function(e){return encodeURIComponent(c(e))},t.escapeJsonPointer=c,t.unescapeJsonPointer=u,t.eachItem=function(e,t){if(Array.isArray(e))for(let r of e)t(r);else t(e)},t.mergeEvaluated={props:p({mergeNames:(e,t,r)=>e.if(o._`${r} !== true && ${t} !== undefined`,(()=>{e.if(o._`${t} === true`,(()=>e.assign(r,!0)),(()=>e.assign(r,o._`${r} || {}`).code(o._`Object.assign(${r}, ${t})`)))})),mergeToName:(e,t,r)=>e.if(o._`${r} !== true`,(()=>{!0===t?e.assign(r,!0):(e.assign(r,o._`${r} || {}`),f(e,r,t))})),mergeValues:(e,t)=>!0===e||{...e,...t},resultToName:d}),items:p({mergeNames:(e,t,r)=>e.if(o._`${r} !== true && ${t} !== undefined`,(()=>e.assign(r,o._`${t} === true ? true : ${r} > ${t} ? ${r} : ${t}`))),mergeToName:(e,t,r)=>e.if(o._`${r} !== true`,(()=>e.assign(r,!0===t||o._`${r} > ${t} ? ${r} : ${t}`))),mergeValues:(e,t)=>!0===e||Math.max(e,t),resultToName:(e,t)=>e.var("items",t)})},t.evaluatedPropsToName=d,t.setEvaluated=f;let h={};function m(e,t,r=e.opts.strictSchema){if(r){if(t=`strict mode: ${t}`,!0===r)throw Error(t);e.self.logger.warn(t)}}t.useFunc=function(e,t){return e.scopeValue("func",{ref:t,code:h[t.code]||(h[t.code]=new a._Code(t.code))})},(i=n=t.Type||(t.Type={}))[i.Num=0]="Num",i[i.Str=1]="Str",t.getErrorPath=function(e,t,r){if(e instanceof o.Name){let i=t===n.Num;return r?i?o._`"[" + ${e} + "]"`:o._`"['" + ${e} + "']"`:i?o._`"/" + ${e}`:o._`"/" + ${e}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return r?o.getProperty(e).toString():"/"+c(e)},t.checkStrictMode=m},4566:function(e,t){"use strict";function r(e,t){return t.rules.some((t=>n(e,t)))}function n(e,t){var r;return void 0!==e[t.keyword]||(null===(r=t.definition.implements)||void 0===r?void 0:r.some((t=>void 0!==e[t])))}Object.defineProperty(t,"__esModule",{value:!0}),t.shouldUseRule=t.shouldUseGroup=t.schemaHasRulesForType=void 0,t.schemaHasRulesForType=function({schema:e,self:t},n){let i=t.RULES.types[n];return i&&!0!==i&&r(e,i)},t.shouldUseGroup=r,t.shouldUseRule=n},7627:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.boolOrEmptySchema=t.topBoolOrEmptySchema=void 0;let n=r(1885),i=r(4475),o=r(5018),a={message:"boolean schema is false"};function s(e,t){let{gen:r,data:i}=e;n.reportError({gen:r,keyword:"false schema",data:i,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:e},a,void 0,t)}t.topBoolOrEmptySchema=function(e){let{gen:t,schema:r,validateName:n}=e;!1===r?s(e,!1):"object"==typeof r&&!0===r.$async?t.return(o.default.data):(t.assign(i._`${n}.errors`,null),t.return(!0))},t.boolOrEmptySchema=function(e,t){let{gen:r,schema:n}=e;!1===n?(r.var(t,!1),s(e)):r.var(t,!0)}},7927:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.reportTypeError=t.checkDataTypes=t.checkDataType=t.coerceAndCheckDataType=t.getJSONTypes=t.getSchemaTypes=t.DataType=void 0;let o=r(3664),a=r(4566),s=r(1885),l=r(4475),c=r(6124);function u(e){let t=Array.isArray(e)?e:e?[e]:[];if(t.every(o.isJSONType))return t;throw Error("type must be JSONType or JSONType[]: "+t.join(","))}(i=n=t.DataType||(t.DataType={}))[i.Correct=0]="Correct",i[i.Wrong=1]="Wrong",t.getSchemaTypes=function(e){let t=u(e.type);if(t.includes("null")){if(!1===e.nullable)throw Error("type: null contradicts nullable: false")}else{if(!t.length&&void 0!==e.nullable)throw Error('"nullable" cannot be used without "type"');!0===e.nullable&&t.push("null")}return t},t.getJSONTypes=u,t.coerceAndCheckDataType=function(e,t){var r,i;let{gen:o,data:s,opts:c}=e,u=(r=t,(i=c.coerceTypes)?r.filter((e=>p.has(e)||"array"===i&&"array"===e)):[]),d=t.length>0&&!(0===u.length&&1===t.length&&a.schemaHasRulesForType(e,t[0]));if(d){let r=f(t,s,c.strictNumbers,n.Wrong);o.if(r,(()=>{u.length?function(e,t,r){let{gen:n,data:i,opts:o}=e,a=n.let("dataType",l._`typeof ${i}`),s=n.let("coerced",l._`undefined`);for(let e of("array"===o.coerceTypes&&n.if(l._`${a} == 'object' && Array.isArray(${i}) && ${i}.length == 1`,(()=>n.assign(i,l._`${i}[0]`).assign(a,l._`typeof ${i}`).if(f(t,i,o.strictNumbers),(()=>n.assign(s,i))))),n.if(l._`${s} !== undefined`),r))(p.has(e)||"array"===e&&"array"===o.coerceTypes)&&c(e);function c(e){switch(e){case"string":return void n.elseIf(l._`${a} == "number" || ${a} == "boolean"`).assign(s,l._`"" + ${i}`).elseIf(l._`${i} === null`).assign(s,l._`""`);case"number":return void n.elseIf(l._`${a} == "boolean" || ${i} === null + || (${a} == "string" && ${i} && ${i} == +${i})`).assign(s,l._`+${i}`);case"integer":return void n.elseIf(l._`${a} === "boolean" || ${i} === null + || (${a} === "string" && ${i} && ${i} == +${i} && !(${i} % 1))`).assign(s,l._`+${i}`);case"boolean":return void n.elseIf(l._`${i} === "false" || ${i} === 0 || ${i} === null`).assign(s,!1).elseIf(l._`${i} === "true" || ${i} === 1`).assign(s,!0);case"null":return n.elseIf(l._`${i} === "" || ${i} === 0 || ${i} === false`),void n.assign(s,null);case"array":n.elseIf(l._`${a} === "string" || ${a} === "number" + || ${a} === "boolean" || ${i} === null`).assign(s,l._`[${i}]`)}}n.else(),m(e),n.endIf(),n.if(l._`${s} !== undefined`,(()=>{n.assign(i,s),function({gen:e,parentData:t,parentDataProperty:r},n){e.if(l._`${t} !== undefined`,(()=>e.assign(l._`${t}[${r}]`,n)))}(e,s)}))}(e,t,u):m(e)}))}return d};let p=new Set(["string","number","integer","boolean","null"]);function d(e,t,r,i=n.Correct){let o,a=i===n.Correct?l.operators.EQ:l.operators.NEQ;switch(e){case"null":return l._`${t} ${a} null`;case"array":o=l._`Array.isArray(${t})`;break;case"object":o=l._`${t} && typeof ${t} == "object" && !Array.isArray(${t})`;break;case"integer":o=s(l._`!(${t} % 1) && !isNaN(${t})`);break;case"number":o=s();break;default:return l._`typeof ${t} ${a} ${e}`}return i===n.Correct?o:l.not(o);function s(e=l.nil){return l.and(l._`typeof ${t} == "number"`,e,r?l._`isFinite(${t})`:l.nil)}}function f(e,t,r,n){if(1===e.length)return d(e[0],t,r,n);let i,o=c.toHash(e);if(o.array&&o.object){let e=l._`typeof ${t} != "object"`;i=o.null?e:l._`!${t} || ${e}`,delete o.null,delete o.array,delete o.object}else i=l.nil;for(let e in o.number&&delete o.integer,o)i=l.and(i,d(e,t,r,n));return i}t.checkDataType=d,t.checkDataTypes=f;let h={message:({schema:e})=>`must be ${e}`,params:({schema:e,schemaValue:t})=>"string"==typeof e?l._`{type: ${e}}`:l._`{type: ${t}}`};function m(e){let t=function(e){let{gen:t,data:r,schema:n}=e,i=c.schemaRefOrVal(e,n,"type");return{gen:t,keyword:"type",data:r,schema:n.type,schemaCode:i,schemaValue:i,parentSchema:n,params:{},it:e}}(e);s.reportError(t,h)}t.reportTypeError=m},2537:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.assignDefaults=void 0;let n=r(4475),i=r(6124);function o(e,t,r){let{gen:o,compositeRule:a,data:s,opts:l}=e;if(void 0===r)return;let c=n._`${s}${n.getProperty(t)}`;if(a)return void i.checkStrictMode(e,`default is ignored for: ${c}`);let u=n._`${c} === undefined`;"empty"===l.useDefaults&&(u=n._`${u} || ${c} === null || ${c} === ""`),o.if(u,n._`${c} = ${n.stringify(r)}`)}t.assignDefaults=function(e,t){let{properties:r,items:n}=e.schema;if("object"===t&&r)for(let t in r)o(e,t,r[t].default);else"array"===t&&Array.isArray(n)&&n.forEach(((t,r)=>o(e,r,t.default)))}},1321:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getData=t.KeywordCxt=t.validateFunctionCode=void 0;let n=r(7627),i=r(7927),o=r(4566),a=r(7927),s=r(2537),l=r(6488),c=r(4688),u=r(4475),p=r(5018),d=r(9826),f=r(6124),h=r(1885);function m({gen:e,validateName:t,schema:r,schemaEnv:n,opts:i},o){i.code.es5?e.func(t,u._`${p.default.data}, ${p.default.valCxt}`,n.$async,(()=>{e.code(u._`"use strict"; ${g(r,i)}`),function(e,t){e.if(p.default.valCxt,(()=>{e.var(p.default.instancePath,u._`${p.default.valCxt}.${p.default.instancePath}`),e.var(p.default.parentData,u._`${p.default.valCxt}.${p.default.parentData}`),e.var(p.default.parentDataProperty,u._`${p.default.valCxt}.${p.default.parentDataProperty}`),e.var(p.default.rootData,u._`${p.default.valCxt}.${p.default.rootData}`),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`${p.default.valCxt}.${p.default.dynamicAnchors}`)}),(()=>{e.var(p.default.instancePath,u._`""`),e.var(p.default.parentData,u._`undefined`),e.var(p.default.parentDataProperty,u._`undefined`),e.var(p.default.rootData,p.default.data),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`{}`)}))}(e,i),e.code(o)})):e.func(t,u._`${p.default.data}, ${u._`{${p.default.instancePath}="", ${p.default.parentData}, ${p.default.parentDataProperty}, ${p.default.rootData}=${p.default.data}${i.dynamicRef?u._`, ${p.default.dynamicAnchors}={}`:u.nil}}={}`}`,n.$async,(()=>e.code(g(r,i)).code(o)))}function g(e,t){let r="object"==typeof e&&e[t.schemaId];return r&&(t.code.source||t.code.process)?u._`/*# sourceURL=${r} */`:u.nil}function y({schema:e,self:t}){if("boolean"==typeof e)return!e;for(let r in e)if(t.RULES.all[r])return!0;return!1}function v(e){return"boolean"!=typeof e.schema}function b(e){f.checkUnknownRules(e),function(e){let{schema:t,errSchemaPath:r,opts:n,self:i}=e;t.$ref&&n.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(t,i.RULES)&&i.logger.warn(`$ref: keywords ignored in schema at path "${r}"`)}(e)}function x(e,t){if(e.opts.jtd)return k(e,[],!1,t);let r=i.getSchemaTypes(e.schema);k(e,r,!i.coerceAndCheckDataType(e,r),t)}function w({gen:e,schemaEnv:t,schema:r,errSchemaPath:n,opts:i}){let o=r.$comment;if(!0===i.$comment)e.code(u._`${p.default.self}.logger.log(${o})`);else if("function"==typeof i.$comment){let r=u.str`${n}/$comment`,i=e.scopeValue("root",{ref:t.root});e.code(u._`${p.default.self}.opts.$comment(${o}, ${r}, ${i}.schema)`)}}function k(e,t,r,n){var i,s,l,c,d,h;let{gen:m,schema:g,data:y,allErrors:v,opts:b,self:x}=e,{RULES:w}=x;function k(i){o.shouldUseGroup(g,i)&&(i.type?(m.if(a.checkDataType(i.type,y,b.strictNumbers)),_(e,i),1===t.length&&t[0]===i.type&&r&&(m.else(),a.reportTypeError(e)),m.endIf()):_(e,i),v||m.if(u._`${p.default.errors} === ${n||0}`))}!g.$ref||!b.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(g,w)?(b.jtd||(s=t,!(i=e).schemaEnv.meta&&i.opts.strictTypes&&(l=i,(c=s).length&&(l.dataTypes.length?(c.forEach((e=>{O(l.dataTypes,e)||S(l,`type "${e}" not allowed by context "${l.dataTypes.join(",")}"`)})),l.dataTypes=l.dataTypes.filter((e=>O(c,e)))):l.dataTypes=c),i.opts.allowUnionTypes||(d=i,(h=s).length>1&&(2!==h.length||!h.includes("null"))&&S(d,"use allowUnionTypes to allow union type keyword")),function(e,t){let r=e.self.RULES.all;for(let n in r){let i=r[n];if("object"==typeof i&&o.shouldUseRule(e.schema,i)){let{type:r}=i.definition;r.length&&!r.some((e=>{var r,n;return n=e,(r=t).includes(n)||"number"===n&&r.includes("integer")}))&&S(e,`missing type "${r.join(",")}" for keyword "${n}"`)}}}(i,i.dataTypes))),m.block((()=>{for(let e of w.rules)k(e);k(w.post)}))):m.block((()=>P(e,"$ref",w.all.$ref.definition)))}function _(e,t){let{gen:r,schema:n,opts:{useDefaults:i}}=e;i&&s.assignDefaults(e,t.type),r.block((()=>{for(let r of t.rules)o.shouldUseRule(n,r)&&P(e,r.keyword,r.definition,t.type)}))}function O(e,t){return e.includes(t)||"integer"===t&&e.includes("number")}function S(e,t){t+=` at "${e.schemaEnv.baseId+e.errSchemaPath}" (strictTypes)`,f.checkStrictMode(e,t,e.opts.strictTypes)}t.validateFunctionCode=function(e){v(e)&&(b(e),y(e))?function(e){let{schema:t,opts:r,gen:n}=e;m(e,(()=>{r.$comment&&t.$comment&&w(e),function(e){let{schema:t,opts:r}=e;void 0!==t.default&&r.useDefaults&&r.strictSchema&&f.checkStrictMode(e,"default is ignored in the schema root")}(e),n.let(p.default.vErrors,null),n.let(p.default.errors,0),r.unevaluated&&function(e){let{gen:t,validateName:r}=e;e.evaluated=t.const("evaluated",u._`${r}.evaluated`),t.if(u._`${e.evaluated}.dynamicProps`,(()=>t.assign(u._`${e.evaluated}.props`,u._`undefined`))),t.if(u._`${e.evaluated}.dynamicItems`,(()=>t.assign(u._`${e.evaluated}.items`,u._`undefined`)))}(e),x(e),function(e){let{gen:t,schemaEnv:r,validateName:n,ValidationError:i,opts:o}=e;r.$async?t.if(u._`${p.default.errors} === 0`,(()=>t.return(p.default.data)),(()=>t.throw(u._`new ${i}(${p.default.vErrors})`))):(t.assign(u._`${n}.errors`,p.default.vErrors),o.unevaluated&&function({gen:e,evaluated:t,props:r,items:n}){r instanceof u.Name&&e.assign(u._`${t}.props`,r),n instanceof u.Name&&e.assign(u._`${t}.items`,n)}(e),t.return(u._`${p.default.errors} === 0`))}(e)}))}(e):m(e,(()=>n.topBoolOrEmptySchema(e)))};class E{constructor(e,t,r){if(l.validateKeywordUsage(e,t,r),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=r,this.data=e.data,this.schema=e.schema[r],this.$data=t.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=f.schemaRefOrVal(e,this.schema,r,this.$data),this.schemaType=t.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=t,this.$data)this.schemaCode=e.gen.const("vSchema",C(this.$data,e));else if(this.schemaCode=this.schemaValue,!l.validSchemaType(this.schema,t.schemaType,t.allowUndefined))throw Error(`${r} value must be ${JSON.stringify(t.schemaType)}`);("code"in t?t.trackErrors:!1!==t.errors)&&(this.errsCount=e.gen.const("_errs",p.default.errors))}result(e,t,r){this.gen.if(u.not(e)),r?r():this.error(),t?(this.gen.else(),t(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,t){this.result(e,void 0,t)}fail(e){if(void 0===e)return this.error(),void(this.allErrors||this.gen.if(!1));this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);let{schemaCode:t}=this;this.fail(u._`${t} !== undefined && (${u.or(this.invalid$data(),e)})`)}error(e,t,r){if(t)return this.setParams(t),this._error(e,r),void this.setParams({});this._error(e,r)}_error(e,t){(e?h.reportExtraError:h.reportError)(this,this.def.error,t)}$dataError(){h.reportError(this,this.def.$dataError||h.keyword$DataError)}reset(){if(void 0===this.errsCount)throw Error('add "trackErrors" to keyword definition');h.resetErrorsCount(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,t){t?Object.assign(this.params,e):this.params=e}block$data(e,t,r=u.nil){this.gen.block((()=>{this.check$data(e,r),t()}))}check$data(e=u.nil,t=u.nil){if(!this.$data)return;let{gen:r,schemaCode:n,schemaType:i,def:o}=this;r.if(u.or(u._`${n} === undefined`,t)),e!==u.nil&&r.assign(e,!0),(i.length||o.validateSchema)&&(r.elseIf(this.invalid$data()),this.$dataError(),e!==u.nil&&r.assign(e,!1)),r.else()}invalid$data(){let{gen:e,schemaCode:t,schemaType:r,def:n,it:i}=this;return u.or(function(){if(r.length){if(!(t instanceof u.Name))throw Error("ajv implementation error");let e=Array.isArray(r)?r:[r];return u._`${a.checkDataTypes(e,t,i.opts.strictNumbers,a.DataType.Wrong)}`}return u.nil}(),function(){if(n.validateSchema){let r=e.scopeValue("validate$data",{ref:n.validateSchema});return u._`!${r}(${t})`}return u.nil}())}subschema(e,t){var r,i;let o=c.getSubschema(this.it,e);c.extendSubschemaData(o,this.it,e),c.extendSubschemaMode(o,e);let a={...this.it,...o,items:void 0,props:void 0};return i=t,v(r=a)&&(b(r),y(r))?function(e,t){let{schema:r,gen:n,opts:i}=e;i.$comment&&r.$comment&&w(e),function(e){let t=e.schema[e.opts.schemaId];t&&(e.baseId=d.resolveUrl(e.baseId,t))}(e),function(e){if(e.schema.$async&&!e.schemaEnv.$async)throw Error("async schema in sync schema")}(e);let o=n.const("_errs",p.default.errors);x(e,o),n.var(t,u._`${o} === ${p.default.errors}`)}(r,i):n.boolOrEmptySchema(r,i),a}mergeEvaluated(e,t){let{it:r,gen:n}=this;r.opts.unevaluated&&(!0!==r.props&&void 0!==e.props&&(r.props=f.mergeEvaluated.props(n,e.props,r.props,t)),!0!==r.items&&void 0!==e.items&&(r.items=f.mergeEvaluated.items(n,e.items,r.items,t)))}mergeValidEvaluated(e,t){let{it:r,gen:n}=this;if(r.opts.unevaluated&&(!0!==r.props||!0!==r.items))return n.if(t,(()=>this.mergeEvaluated(e,u.Name))),!0}}function P(e,t,r,n){let i=new E(e,r,t);"code"in r?r.code(i,n):i.$data&&r.validate?l.funcKeywordCode(i,r):"macro"in r?l.macroKeywordCode(i,r):(r.compile||r.validate)&&l.funcKeywordCode(i,r)}t.KeywordCxt=E;let A=/^\/(?:[^~]|~0|~1)*$/,$=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function C(e,{dataLevel:t,dataNames:r,dataPathArr:n}){let i,o;if(""===e)return p.default.rootData;if("/"===e[0]){if(!A.test(e))throw Error(`Invalid JSON-pointer: ${e}`);i=e,o=p.default.rootData}else{let a=$.exec(e);if(!a)throw Error(`Invalid JSON-pointer: ${e}`);let s=+a[1];if("#"===(i=a[2])){if(s>=t)throw Error(l("property/index",s));return n[t-s]}if(s>t)throw Error(l("data",s));if(o=r[t-s],!i)return o}let a=o,s=i.split("/");for(let e of s)e&&(a=u._`${a} && ${o=u._`${o}${u.getProperty(f.unescapeJsonPointer(e))}`}`);return a;function l(e,r){return`Cannot access ${e} ${r} levels up, current level is ${t}`}}t.getData=C},6488:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateKeywordUsage=t.validSchemaType=t.funcKeywordCode=t.macroKeywordCode=void 0;let n=r(4475),i=r(5018),o=r(8619),a=r(1885);function s(e){let{gen:t,data:r,it:i}=e;t.if(i.parentData,(()=>t.assign(r,n._`${i.parentData}[${i.parentDataProperty}]`)))}function l(e,t,r){if(void 0===r)throw Error(`keyword "${t}" failed to compile`);return e.scopeValue("keyword","function"==typeof r?{ref:r}:{ref:r,code:n.stringify(r)})}t.macroKeywordCode=function(e,t){let{gen:r,keyword:i,schema:o,parentSchema:a,it:s}=e,c=t.macro.call(s.self,o,a,s),u=l(r,i,c);!1!==s.opts.validateSchema&&s.self.validateSchema(c,!0);let p=r.name("valid");e.subschema({schema:c,schemaPath:n.nil,errSchemaPath:`${s.errSchemaPath}/${i}`,topSchemaRef:u,compositeRule:!0},p),e.pass(p,(()=>e.error(!0)))},t.funcKeywordCode=function(e,t){var r;let{gen:c,keyword:u,schema:p,parentSchema:d,$data:f,it:h}=e;!function({schemaEnv:e},t){if(t.async&&!e.$async)throw Error("async keyword in sync schema")}(h,t);let m=!f&&t.compile?t.compile.call(h.self,p,d,h):t.validate,g=l(c,u,m),y=c.let("valid");function v(r=(t.async?n._`await `:n.nil)){let a=h.opts.passContext?i.default.this:i.default.self,s=!("compile"in t&&!f||!1===t.schema);c.assign(y,n._`${r}${o.callValidateCode(e,g,a,s)}`,t.modifying)}function b(e){var r;c.if(n.not(null!==(r=t.valid)&&void 0!==r?r:y),e)}e.block$data(y,(function(){if(!1===t.errors)v(),t.modifying&&s(e),b((()=>e.error()));else{let r=t.async?function(){let e=c.let("ruleErrs",null);return c.try((()=>v(n._`await `)),(t=>c.assign(y,!1).if(n._`${t} instanceof ${h.ValidationError}`,(()=>c.assign(e,n._`${t}.errors`)),(()=>c.throw(t))))),e}():function(){let e=n._`${g}.errors`;return c.assign(e,null),v(n.nil),e}();t.modifying&&s(e),b((()=>function(e,t){let{gen:r}=e;r.if(n._`Array.isArray(${t})`,(()=>{r.assign(i.default.vErrors,n._`${i.default.vErrors} === null ? ${t} : ${i.default.vErrors}.concat(${t})`).assign(i.default.errors,n._`${i.default.vErrors}.length`),a.extendErrors(e)}),(()=>e.error()))}(e,r)))}})),e.ok(null!==(r=t.valid)&&void 0!==r?r:y)},t.validSchemaType=function(e,t,r=!1){return!t.length||t.some((t=>"array"===t?Array.isArray(e):"object"===t?e&&"object"==typeof e&&!Array.isArray(e):typeof e==t||r&&void 0===e))},t.validateKeywordUsage=function({schema:e,opts:t,self:r,errSchemaPath:n},i,o){if(Array.isArray(i.keyword)?!i.keyword.includes(o):i.keyword!==o)throw Error("ajv implementation error");let a=i.dependencies;if(null==a?void 0:a.some((t=>!Object.prototype.hasOwnProperty.call(e,t))))throw Error(`parent schema must have dependencies of ${o}: ${a.join(",")}`);if(i.validateSchema&&!i.validateSchema(e[o])){let e=`keyword "${o}" value is invalid at path "${n}": `+r.errorsText(i.validateSchema.errors);if("log"!==t.validateSchema)throw Error(e);r.logger.error(e)}}},4688:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendSubschemaMode=t.extendSubschemaData=t.getSubschema=void 0;let n=r(4475),i=r(6124);t.getSubschema=function(e,{keyword:t,schemaProp:r,schema:o,schemaPath:a,errSchemaPath:s,topSchemaRef:l}){if(void 0!==t&&void 0!==o)throw Error('both "keyword" and "schema" passed, only one allowed');if(void 0!==t){let o=e.schema[t];return void 0===r?{schema:o,schemaPath:n._`${e.schemaPath}${n.getProperty(t)}`,errSchemaPath:`${e.errSchemaPath}/${t}`}:{schema:o[r],schemaPath:n._`${e.schemaPath}${n.getProperty(t)}${n.getProperty(r)}`,errSchemaPath:`${e.errSchemaPath}/${t}/${i.escapeFragment(r)}`}}if(void 0!==o){if(void 0===a||void 0===s||void 0===l)throw Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"');return{schema:o,schemaPath:a,topSchemaRef:l,errSchemaPath:s}}throw Error('either "keyword" or "schema" must be passed')},t.extendSubschemaData=function(e,t,{dataProp:r,dataPropType:o,data:a,dataTypes:s,propertyName:l}){if(void 0!==a&&void 0!==r)throw Error('both "data" and "dataProp" passed, only one allowed');let{gen:c}=t;if(void 0!==r){let{errorPath:a,dataPathArr:s,opts:l}=t;u(c.let("data",n._`${t.data}${n.getProperty(r)}`,!0)),e.errorPath=n.str`${a}${i.getErrorPath(r,o,l.jsPropertySyntax)}`,e.parentDataProperty=n._`${r}`,e.dataPathArr=[...s,e.parentDataProperty]}function u(r){e.data=r,e.dataLevel=t.dataLevel+1,e.dataTypes=[],t.definedProperties=new Set,e.parentData=t.data,e.dataNames=[...t.dataNames,r]}void 0!==a&&(u(a instanceof n.Name?a:c.let("data",a,!0)),void 0!==l&&(e.propertyName=l)),s&&(e.dataTypes=s)},t.extendSubschemaMode=function(e,{jtdDiscriminator:t,jtdMetadata:r,compositeRule:n,createErrors:i,allErrors:o}){void 0!==n&&(e.compositeRule=n),void 0!==i&&(e.createErrors=i),void 0!==o&&(e.allErrors=o),e.jtdDiscriminator=t,e.jtdMetadata=r}},3325:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;var n=r(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return n.KeywordCxt}});var i=r(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return i._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return i.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return i.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return i.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return i.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return i.CodeGen}});let o=r(8451),a=r(4143),s=r(3664),l=r(7805),c=r(4475),u=r(9826),p=r(7927),d=r(6124),f=r(425),h=["removeAdditional","useDefaults","coerceTypes"],m=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]);class g{constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...function(e){var t,r,n,i,o,a,s,l,c,u,p,d,f,h,m,g,y,v,b,x,w,k;let _=e.strict,O=null===(t=e.code)||void 0===t?void 0:t.optimize,S=!0===O||void 0===O?1:O||0;return{strictSchema:null===(n=null!==(r=e.strictSchema)&&void 0!==r?r:_)||void 0===n||n,strictNumbers:null===(o=null!==(i=e.strictNumbers)&&void 0!==i?i:_)||void 0===o||o,strictTypes:null!==(s=null!==(a=e.strictTypes)&&void 0!==a?a:_)&&void 0!==s?s:"log",strictTuples:null!==(c=null!==(l=e.strictTuples)&&void 0!==l?l:_)&&void 0!==c?c:"log",strictRequired:null!==(p=null!==(u=e.strictRequired)&&void 0!==u?u:_)&&void 0!==p&&p,code:e.code?{...e.code,optimize:S}:{optimize:S},loopRequired:null!==(d=e.loopRequired)&&void 0!==d?d:200,loopEnum:null!==(f=e.loopEnum)&&void 0!==f?f:200,meta:null===(h=e.meta)||void 0===h||h,messages:null===(m=e.messages)||void 0===m||m,inlineRefs:null===(g=e.inlineRefs)||void 0===g||g,schemaId:null!==(y=e.schemaId)&&void 0!==y?y:"$id",addUsedSchema:null===(v=e.addUsedSchema)||void 0===v||v,validateSchema:null===(b=e.validateSchema)||void 0===b||b,validateFormats:null===(x=e.validateFormats)||void 0===x||x,unicodeRegExp:null===(w=e.unicodeRegExp)||void 0===w||w,int32range:null===(k=e.int32range)||void 0===k||k}}(e)};let{es5:t,lines:r}=this.opts.code;this.scope=new c.ValueScope({scope:{},prefixes:m,es5:t,lines:r}),this.logger=function(e){if(!1===e)return b;if(void 0===e)return console;if(e.log&&e.warn&&e.error)return e;throw Error("logger must implement log, warn and error methods")}(e.logger);let n=e.validateFormats;e.validateFormats=!1,this.RULES=s.getRules(),y.call(this,{errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."},e,"NOT SUPPORTED"),y.call(this,{ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'},e,"DEPRECATED","warn"),this._metaOpts=function(){let e={...this.opts};for(let t of h)delete e[t];return e}.call(this),e.formats&&function(){for(let e in this.opts.formats){let t=this.opts.formats[e];t&&this.addFormat(e,t)}}.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&function(e){if(Array.isArray(e))this.addVocabulary(e);else for(let t in this.logger.warn("keywords option as map is deprecated, pass array"),e){let r=e[t];r.keyword||(r.keyword=t),this.addKeyword(r)}}.call(this,e.keywords),"object"==typeof e.meta&&this.addMetaSchema(e.meta),function(){let e=this.opts.schemas;if(e)if(Array.isArray(e))this.addSchema(e);else for(let t in e)this.addSchema(e[t],t)}.call(this),e.validateFormats=n}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){let{$data:e,meta:t,schemaId:r}=this.opts,n=f;"id"===r&&((n={...f}).id=n.$id,delete n.$id),t&&e&&this.addMetaSchema(n,n[r],!1)}defaultMeta(){let{meta:e,schemaId:t}=this.opts;return this.opts.defaultMeta="object"==typeof e?e[t]||e:void 0}validate(e,t){let r;if("string"==typeof e){if(!(r=this.getSchema(e)))throw Error(`no schema with key or ref "${e}"`)}else r=this.compile(e);let n=r(t);return"$async"in r||(this.errors=r.errors),n}compile(e,t){let r=this._addSchema(e,t);return r.validate||this._compileSchemaEnv(r)}compileAsync(e,t){if("function"!=typeof this.opts.loadSchema)throw Error("options.loadSchema should be a function");let{loadSchema:r}=this.opts;return n.call(this,e,t);async function n(e,t){await i.call(this,e.$schema);let r=this._addSchema(e,t);return r.validate||o.call(this,r)}async function i(e){e&&!this.getSchema(e)&&await n.call(this,{$ref:e},!0)}async function o(e){try{return this._compileSchemaEnv(e)}catch(t){if(!(t instanceof a.default))throw t;return s.call(this,t),await l.call(this,t.missingSchema),o.call(this,e)}}function s({missingSchema:e,missingRef:t}){if(this.refs[e])throw Error(`AnySchema ${e} is loaded but ${t} cannot be resolved`)}async function l(e){let r=await c.call(this,e);this.refs[e]||await i.call(this,r.$schema),this.refs[e]||this.addSchema(r,e,t)}async function c(e){let t=this._loading[e];if(t)return t;try{return await(this._loading[e]=r(e))}finally{delete this._loading[e]}}}addSchema(e,t,r,n=this.opts.validateSchema){if(Array.isArray(e)){for(let t of e)this.addSchema(t,void 0,r,n);return this}let i;if("object"==typeof e){let{schemaId:t}=this.opts;if(void 0!==(i=e[t])&&"string"!=typeof i)throw Error(`schema ${t} must be string`)}return t=u.normalizeId(t||i),this._checkUnique(t),this.schemas[t]=this._addSchema(e,r,t,n,!0),this}addMetaSchema(e,t,r=this.opts.validateSchema){return this.addSchema(e,t,!0,r),this}validateSchema(e,t){if("boolean"==typeof e)return!0;let r;if(void 0!==(r=e.$schema)&&"string"!=typeof r)throw Error("$schema must be a string");if(!(r=r||this.opts.defaultMeta||this.defaultMeta()))return this.logger.warn("meta-schema not available"),this.errors=null,!0;let n=this.validate(r,e);if(!n&&t){let e="schema is invalid: "+this.errorsText();if("log"!==this.opts.validateSchema)throw Error(e);this.logger.error(e)}return n}getSchema(e){let t;for(;"string"==typeof(t=v.call(this,e));)e=t;if(void 0===t){let{schemaId:r}=this.opts,n=new l.SchemaEnv({schema:{},schemaId:r});if(!(t=l.resolveSchema.call(this,n,e)))return;this.refs[e]=t}return t.validate||this._compileSchemaEnv(t)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case"undefined":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case"string":{let t=v.call(this,e);return"object"==typeof t&&this._cache.delete(t.schema),delete this.schemas[e],delete this.refs[e],this}case"object":{this._cache.delete(e);let t=e[this.opts.schemaId];return t&&(t=u.normalizeId(t),delete this.schemas[t],delete this.refs[t]),this}default:throw Error("ajv.removeSchema: invalid parameter")}}addVocabulary(e){for(let t of e)this.addKeyword(t);return this}addKeyword(e,t){let r;if("string"==typeof e)r=e,"object"==typeof t&&(this.logger.warn("these parameters are deprecated, see docs for addKeyword"),t.keyword=r);else{if("object"!=typeof e||void 0!==t)throw Error("invalid addKeywords parameters");if(Array.isArray(r=(t=e).keyword)&&!r.length)throw Error("addKeywords: keyword must be string or non-empty array")}if(w.call(this,r,t),!t)return d.eachItem(r,(e=>k.call(this,e))),this;O.call(this,t);let n={...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)};return d.eachItem(r,0===n.type.length?e=>k.call(this,e,n):e=>n.type.forEach((t=>k.call(this,e,n,t)))),this}getKeyword(e){let t=this.RULES.all[e];return"object"==typeof t?t.definition:!!t}removeKeyword(e){let{RULES:t}=this;for(let r of(delete t.keywords[e],delete t.all[e],t.rules)){let t=r.rules.findIndex((t=>t.keyword===e));t>=0&&r.rules.splice(t,1)}return this}addFormat(e,t){return"string"==typeof t&&(t=RegExp(t)),this.formats[e]=t,this}errorsText(e=this.errors,{separator:t=", ",dataVar:r="data"}={}){return e&&0!==e.length?e.map((e=>`${r}${e.instancePath} ${e.message}`)).reduce(((e,r)=>e+t+r)):"No errors"}$dataMetaSchema(e,t){let r=this.RULES.all;for(let n of(e=JSON.parse(JSON.stringify(e)),t)){let t=n.split("/").slice(1),i=e;for(let e of t)i=i[e];for(let e in r){let t=r[e];if("object"!=typeof t)continue;let{$data:n}=t.definition,o=i[e];n&&o&&(i[e]=E(o))}}return e}_removeAllSchemas(e,t){for(let r in e){let n=e[r];t&&!t.test(r)||("string"==typeof n?delete e[r]:n&&!n.meta&&(this._cache.delete(n.schema),delete e[r]))}}_addSchema(e,t,r,n=this.opts.validateSchema,i=this.opts.addUsedSchema){let o,{schemaId:a}=this.opts;if("object"==typeof e)o=e[a];else{if(this.opts.jtd)throw Error("schema must be object");if("boolean"!=typeof e)throw Error("schema must be object or boolean")}let s=this._cache.get(e);if(void 0!==s)return s;let c=u.getSchemaRefs.call(this,e);return r=u.normalizeId(o||r),s=new l.SchemaEnv({schema:e,schemaId:a,meta:t,baseId:r,localRefs:c}),this._cache.set(s.schema,s),i&&!r.startsWith("#")&&(r&&this._checkUnique(r),this.refs[r]=s),n&&this.validateSchema(e,!0),s}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw Error(`schema with key or id "${e}" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):l.compileSchema.call(this,e),!e.validate)throw Error("ajv implementation error");return e.validate}_compileMetaSchema(e){let t=this.opts;this.opts=this._metaOpts;try{l.compileSchema.call(this,e)}finally{this.opts=t}}}function y(e,t,r,n="error"){for(let i in e){let o=i;o in t&&this.logger[n](`${r}: option ${i}. ${e[o]}`)}}function v(e){return e=u.normalizeId(e),this.schemas[e]||this.refs[e]}t.default=g,g.ValidationError=o.default,g.MissingRefError=a.default;let b={log(){},warn(){},error(){}},x=/^[a-z_$][a-z0-9_$:-]*$/i;function w(e,t){let{RULES:r}=this;if(d.eachItem(e,(e=>{if(r.keywords[e])throw Error(`Keyword ${e} is already defined`);if(!x.test(e))throw Error(`Keyword ${e} has invalid name`)})),t&&t.$data&&!("code"in t)&&!("validate"in t))throw Error('$data keyword must have "code" or "validate" function')}function k(e,t,r){var n;let i=null==t?void 0:t.post;if(r&&i)throw Error('keyword with "post" flag cannot have "type"');let{RULES:o}=this,a=i?o.post:o.rules.find((({type:e})=>e===r));if(a||(a={type:r,rules:[]},o.rules.push(a)),o.keywords[e]=!0,!t)return;let s={keyword:e,definition:{...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)}};t.before?_.call(this,a,s,t.before):a.rules.push(s),o.all[e]=s,null===(n=t.implements)||void 0===n||n.forEach((e=>this.addKeyword(e)))}function _(e,t,r){let n=e.rules.findIndex((e=>e.keyword===r));n>=0?e.rules.splice(n,0,t):(e.rules.push(t),this.logger.warn(`rule ${r} is not defined`))}function O(e){let{metaSchema:t}=e;void 0!==t&&(e.$data&&this.opts.$data&&(t=E(t)),e.validateSchema=this.compile(t,!0))}let S={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function E(e){return{anyOf:[e,S]}}},412:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4063);n.code='require("ajv/dist/runtime/equal").default',t.default=n},5872:function(e,t){"use strict";function r(e){let t,r=e.length,n=0,i=0;for(;i=55296&&t<=56319&&i{var a;return a=o,void r.forRange("i",t.length,c,(t=>{e.subschema({keyword:s,dataProp:t,dataPropType:i.Type.Num},a),l.allErrors||r.if(n.not(a),(()=>r.break()))}))})),e.ok(o)}}t.validateAdditionalItems=o,t.default={keyword:"additionalItems",type:"array",schemaType:["boolean","object"],before:"uniqueItems",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){let{parentSchema:t,it:r}=e,{items:n}=t;Array.isArray(n)?o(e,n):i.checkStrictMode(r,'"additionalItems" is ignored when "items" is not an array of schemas')}}},1422:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(5018),a=r(6124);t.default={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:!0,trackErrors:!0,error:{message:"must NOT have additional properties",params:({params:e})=>i._`{additionalProperty: ${e.additionalProperty}}`},code(e){let{gen:t,parentSchema:r,data:s,errsCount:l,it:c}=e,{schema:u=c.opts.defaultAdditionalProperties}=e;if(!l)throw Error("ajv implementation error");let{allErrors:p,opts:d}=c;if(c.props=!0,"all"!==d.removeAdditional&&a.alwaysValidSchema(c,u))return;let f=n.allSchemaProperties(r.properties),h=n.allSchemaProperties(r.patternProperties);function m(e){t.code(i._`delete ${s}[${e}]`)}function g(r){if("all"===d.removeAdditional||d.removeAdditional&&!1===u)m(r);else{if(!1===u)return e.setParams({additionalProperty:r}),e.error(),void(p||t.break());if("object"==typeof u&&!a.alwaysValidSchema(c,u)){let n=t.name("valid");"failing"===d.removeAdditional?(y(r,n,!1),t.if(i.not(n),(()=>{e.reset(),m(r)}))):(y(r,n),p||t.if(i.not(n),(()=>t.break())))}}}function y(t,r,n){let i={keyword:"additionalProperties",dataProp:t,dataPropType:a.Type.Str};!1===n&&Object.assign(i,{compositeRule:!0,createErrors:!1,allErrors:!1}),e.subschema(i,r)}t.forIn("key",s,(o=>{f.length||h.length?t.if(function(o){let s;if(f.length>8){let e=a.schemaRefOrVal(c,r.properties,"properties");s=n.isOwnProperty(t,e,o)}else s=f.length?i.or(...f.map((e=>i._`${o} === ${e}`))):i.nil;return h.length&&(s=i.or(s,...h.map((t=>i._`${n.usePattern(e,t)}.test(${o})`)))),i.not(s)}(o),(()=>g(o))):g(o)})),e.ok(i._`${l} === ${o.default.errors}`)}}},5716:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:"allOf",schemaType:"array",code(e){let{gen:t,schema:r,it:i}=e;if(!Array.isArray(r))throw Error("ajv implementation error");let o=t.name("valid");r.forEach(((t,r)=>{if(n.alwaysValidSchema(i,t))return;let a=e.subschema({keyword:"allOf",schemaProp:r},o);e.ok(o),e.mergeEvaluated(a)}))}}},1668:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n={keyword:"anyOf",schemaType:"array",trackErrors:!0,code:r(8619).validateUnion,error:{message:"must match a schema in anyOf"}};t.default=n},9564:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:!0,error:{message:({params:{min:e,max:t}})=>void 0===t?n.str`must contain at least ${e} valid item(s)`:n.str`must contain at least ${e} and no more than ${t} valid item(s)`,params:({params:{min:e,max:t}})=>void 0===t?n._`{minContains: ${e}}`:n._`{minContains: ${e}, maxContains: ${t}}`},code(e){let t,r,{gen:o,schema:a,parentSchema:s,data:l,it:c}=e,{minContains:u,maxContains:p}=s;c.opts.next?(t=void 0===u?1:u,r=p):t=1;let d=o.const("len",n._`${l}.length`);if(e.setParams({min:t,max:r}),void 0===r&&0===t)return void i.checkStrictMode(c,'"minContains" == 0 without "maxContains": "contains" keyword ignored');if(void 0!==r&&t>r)return i.checkStrictMode(c,'"minContains" > "maxContains" is always invalid'),void e.fail();if(i.alwaysValidSchema(c,a)){let i=n._`${d} >= ${t}`;return void 0!==r&&(i=n._`${i} && ${d} <= ${r}`),void e.pass(i)}c.items=!0;let f=o.name("valid");if(void 0===r&&1===t)h(f,(()=>o.if(f,(()=>o.break()))));else{o.let(f,!1);let e=o.name("_valid"),i=o.let("count",0);h(e,(()=>o.if(e,(()=>{var e;return e=i,o.code(n._`${e}++`),void(void 0===r?o.if(n._`${e} >= ${t}`,(()=>o.assign(f,!0).break())):(o.if(n._`${e} > ${r}`,(()=>o.assign(f,!1).break())),1===t?o.assign(f,!0):o.if(n._`${e} >= ${t}`,(()=>o.assign(f,!0)))))}))))}function h(t,r){o.forRange("i",0,d,(n=>{e.subschema({keyword:"contains",dataProp:n,dataPropType:i.Type.Num,compositeRule:!0},t),r()}))}e.result(f,(()=>e.reset()))}}},1117:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateSchemaDeps=t.validatePropertyDeps=t.error=void 0;let n=r(4475),i=r(6124),o=r(8619);t.error={message:({params:{property:e,depsCount:t,deps:r}})=>n.str`must have ${1===t?"property":"properties"} ${r} when property ${e} is present`,params:({params:{property:e,depsCount:t,deps:r,missingProperty:i}})=>n._`{property: ${e}, + missingProperty: ${i}, + depsCount: ${t}, + deps: ${r}}`};let a={keyword:"dependencies",type:"object",schemaType:"object",error:t.error,code(e){let[t,r]=function({schema:e}){let t={},r={};for(let n in e)"__proto__"!==n&&((Array.isArray(e[n])?t:r)[n]=e[n]);return[t,r]}(e);s(e,t),l(e,r)}};function s(e,t=e.schema){let{gen:r,data:i,it:a}=e;if(0===Object.keys(t).length)return;let s=r.let("missing");for(let l in t){let c=t[l];if(0===c.length)continue;let u=o.propertyInData(r,i,l,a.opts.ownProperties);e.setParams({property:l,depsCount:c.length,deps:c.join(", ")}),a.allErrors?r.if(u,(()=>{for(let t of c)o.checkReportMissingProp(e,t)})):(r.if(n._`${u} && (${o.checkMissingProp(e,c,s)})`),o.reportMissingProp(e,s),r.else())}}function l(e,t=e.schema){let{gen:r,data:n,keyword:a,it:s}=e,l=r.name("valid");for(let c in t)i.alwaysValidSchema(s,t[c])||(r.if(o.propertyInData(r,n,c,s.opts.ownProperties),(()=>{let t=e.subschema({keyword:a,schemaProp:c},l);e.mergeValidEvaluated(t,l)}),(()=>r.var(l,!0))),e.ok(l))}t.validatePropertyDeps=s,t.validateSchemaDeps=l,t.default=a},5184:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);function o(e,t){let r=e.schema[t];return void 0!==r&&!i.alwaysValidSchema(e,r)}t.default={keyword:"if",schemaType:["object","boolean"],trackErrors:!0,error:{message:({params:e})=>n.str`must match "${e.ifClause}" schema`,params:({params:e})=>n._`{failingKeyword: ${e.ifClause}}`},code(e){let{gen:t,parentSchema:r,it:a}=e;void 0===r.then&&void 0===r.else&&i.checkStrictMode(a,'"if" without "then" and "else" is ignored');let s=o(a,"then"),l=o(a,"else");if(!s&&!l)return;let c=t.let("valid",!0),u=t.name("_valid");if(function(){let t=e.subschema({keyword:"if",compositeRule:!0,createErrors:!1,allErrors:!1},u);e.mergeEvaluated(t)}(),e.reset(),s&&l){let r=t.let("ifClause");e.setParams({ifClause:r}),t.if(u,p("then",r),p("else",r))}else s?t.if(u,p("then")):t.if(n.not(u),p("else"));function p(r,i){return()=>{let o=e.subschema({keyword:r},u);t.assign(c,u),e.mergeValidEvaluated(o,c),i?t.assign(i,n._`${r}`):e.setParams({ifClause:r})}}e.pass(c,(()=>e.error(!0)))}}},9616:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(3074),i=r(6988),o=r(6348),a=r(9822),s=r(9564),l=r(1117),c=r(4002),u=r(1422),p=r(9690),d=r(9883),f=r(8435),h=r(1668),m=r(9684),g=r(5716),y=r(5184),v=r(5642);t.default=function(e=!1){let t=[f.default,h.default,m.default,g.default,y.default,v.default,c.default,u.default,l.default,p.default,d.default];return e?t.push(i.default,a.default):t.push(n.default,o.default),t.push(s.default),t}},6348:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateTuple=void 0;let n=r(4475),i=r(6124),o=r(8619);function a(e,t,r=e.schema){let{gen:o,parentSchema:a,data:s,keyword:l,it:c}=e;!function(e){let{opts:n,errSchemaPath:o}=c,a=r.length,s=a===e.minItems&&(a===e.maxItems||!1===e[t]);if(n.strictTuples&&!s){let e=`"${l}" is ${a}-tuple, but minItems or maxItems/${t} are not specified or different at path "${o}"`;i.checkStrictMode(c,e,n.strictTuples)}}(a),c.opts.unevaluated&&r.length&&!0!==c.items&&(c.items=i.mergeEvaluated.items(o,r.length,c.items));let u=o.name("valid"),p=o.const("len",n._`${s}.length`);r.forEach(((t,r)=>{i.alwaysValidSchema(c,t)||(o.if(n._`${p} > ${r}`,(()=>e.subschema({keyword:l,schemaProp:r,dataProp:r},u))),e.ok(u))}))}t.validateTuple=a,t.default={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(e){let{schema:t,it:r}=e;if(Array.isArray(t))return a(e,"additionalItems",t);r.items=!0,i.alwaysValidSchema(r,t)||e.ok(o.validateArray(e))}}},9822:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(8619),a=r(3074);t.default={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){let{schema:t,parentSchema:r,it:n}=e,{prefixItems:s}=r;n.items=!0,i.alwaysValidSchema(n,t)||(s?a.validateAdditionalItems(e,s):e.ok(o.validateArray(e)))}}},8435:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:"not",schemaType:["object","boolean"],trackErrors:!0,code(e){let{gen:t,schema:r,it:i}=e;if(n.alwaysValidSchema(i,r))return void e.fail();let o=t.name("valid");e.subschema({keyword:"not",compositeRule:!0,createErrors:!1,allErrors:!1},o),e.result(o,(()=>e.error()),(()=>e.reset()))},error:{message:"must NOT be valid"}}},9684:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"oneOf",schemaType:"array",trackErrors:!0,error:{message:"must match exactly one schema in oneOf",params:({params:e})=>n._`{passingSchemas: ${e.passing}}`},code(e){let{gen:t,schema:r,parentSchema:o,it:a}=e;if(!Array.isArray(r))throw Error("ajv implementation error");if(a.opts.discriminator&&o.discriminator)return;let s=r,l=t.let("valid",!1),c=t.let("passing",null),u=t.name("_valid");e.setParams({passing:c}),t.block((function(){s.forEach(((r,o)=>{let s;i.alwaysValidSchema(a,r)?t.var(u,!0):s=e.subschema({keyword:"oneOf",schemaProp:o,compositeRule:!0},u),o>0&&t.if(n._`${u} && ${l}`).assign(l,!1).assign(c,n._`[${c}, ${o}]`).else(),t.if(u,(()=>{t.assign(l,!0),t.assign(c,o),s&&e.mergeEvaluated(s,n.Name)}))}))})),e.result(l,(()=>e.reset()),(()=>e.error(!0)))}}},9883:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(6124),a=r(6124);t.default={keyword:"patternProperties",type:"object",schemaType:"object",code(e){let{gen:t,schema:r,data:s,parentSchema:l,it:c}=e,{opts:u}=c,p=n.allSchemaProperties(r),d=p.filter((e=>o.alwaysValidSchema(c,r[e])));if(0===p.length||d.length===p.length&&(!c.opts.unevaluated||!0===c.props))return;let f=u.strictSchema&&!u.allowMatchingProperties&&l.properties,h=t.name("valid");!0===c.props||c.props instanceof i.Name||(c.props=a.evaluatedPropsToName(t,c.props));let{props:m}=c;function g(e){for(let t in f)RegExp(e).test(t)&&o.checkStrictMode(c,`property ${t} matches pattern ${e} (use allowMatchingProperties)`)}function y(r){t.forIn("key",s,(o=>{t.if(i._`${n.usePattern(e,r)}.test(${o})`,(()=>{let n=d.includes(r);n||e.subschema({keyword:"patternProperties",schemaProp:r,dataProp:o,dataPropType:a.Type.Str},h),c.opts.unevaluated&&!0!==m?t.assign(i._`${m}[${o}]`,!0):n||c.allErrors||t.if(i.not(h),(()=>t.break()))}))}))}!function(){for(let e of p)f&&g(e),c.allErrors?y(e):(t.var(h,!0),y(e),t.if(h))}()}}},6988:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6348);t.default={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:e=>n.validateTuple(e,"items")}},9690:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(1321),i=r(8619),o=r(6124),a=r(1422);t.default={keyword:"properties",type:"object",schemaType:"object",code(e){let{gen:t,schema:r,parentSchema:s,data:l,it:c}=e;("all"===c.opts.removeAdditional&&void 0===s.additionalProperties||!1===c.opts.defaultAdditionalProperties)&&a.default.code(new n.KeywordCxt(c,a.default,"additionalProperties"));let u=i.allSchemaProperties(r);for(let e of u)c.definedProperties.add(e);c.opts.unevaluated&&u.length&&!0!==c.props&&(c.props=o.mergeEvaluated.props(t,o.toHash(u),c.props));let p=u.filter((e=>!o.alwaysValidSchema(c,r[e])));if(0===p.length)return;let d=t.name("valid");for(let r of p)f(r)?h(r):(t.if(i.propertyInData(t,l,r,c.opts.ownProperties)),h(r),c.allErrors||t.else().var(d,!0),t.endIf()),e.it.definedProperties.add(r),e.ok(d);function f(e){return c.opts.useDefaults&&!c.compositeRule&&void 0!==r[e].default}function h(t){e.subschema({keyword:"properties",schemaProp:t,dataProp:t},d)}}}},4002:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:{message:"property name must be valid",params:({params:e})=>n._`{propertyName: ${e.propertyName}}`},code(e){let{gen:t,schema:r,data:o,it:a}=e;if(i.alwaysValidSchema(a,r))return;let s=t.name("valid");t.forIn("key",o,(r=>{e.setParams({propertyName:r}),e.subschema({keyword:"propertyNames",data:r,dataTypes:["string"],propertyName:r,compositeRule:!0},s),t.if(n.not(s),(()=>{e.error(!0),a.allErrors||t.break()}))})),e.ok(s)}}},5642:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:["then","else"],schemaType:["object","boolean"],code({keyword:e,parentSchema:t,it:r}){void 0===t.if&&n.checkStrictMode(r,`"${e}" without "if" is ignored`)}}},8619:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateUnion=t.validateArray=t.usePattern=t.callValidateCode=t.schemaProperties=t.allSchemaProperties=t.noPropertyInData=t.propertyInData=t.isOwnProperty=t.hasPropFunc=t.reportMissingProp=t.checkMissingProp=t.checkReportMissingProp=void 0;let n=r(4475),i=r(6124),o=r(5018);function a(e){return e.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:n._`Object.prototype.hasOwnProperty`})}function s(e,t,r){return n._`${a(e)}.call(${t}, ${r})`}function l(e,t,r,i){let o=n._`${t}${n.getProperty(r)} === undefined`;return i?n.or(o,n.not(s(e,t,r))):o}function c(e){return e?Object.keys(e).filter((e=>"__proto__"!==e)):[]}t.checkReportMissingProp=function(e,t){let{gen:r,data:i,it:o}=e;r.if(l(r,i,t,o.opts.ownProperties),(()=>{e.setParams({missingProperty:n._`${t}`},!0),e.error()}))},t.checkMissingProp=function({gen:e,data:t,it:{opts:r}},i,o){return n.or(...i.map((i=>n.and(l(e,t,i,r.ownProperties),n._`${o} = ${i}`))))},t.reportMissingProp=function(e,t){e.setParams({missingProperty:t},!0),e.error()},t.hasPropFunc=a,t.isOwnProperty=s,t.propertyInData=function(e,t,r,i){let o=n._`${t}${n.getProperty(r)} !== undefined`;return i?n._`${o} && ${s(e,t,r)}`:o},t.noPropertyInData=l,t.allSchemaProperties=c,t.schemaProperties=function(e,t){return c(t).filter((r=>!i.alwaysValidSchema(e,t[r])))},t.callValidateCode=function({schemaCode:e,data:t,it:{gen:r,topSchemaRef:i,schemaPath:a,errorPath:s},it:l},c,u,p){let d=p?n._`${e}, ${t}, ${i}${a}`:t,f=[[o.default.instancePath,n.strConcat(o.default.instancePath,s)],[o.default.parentData,l.parentData],[o.default.parentDataProperty,l.parentDataProperty],[o.default.rootData,o.default.rootData]];l.opts.dynamicRef&&f.push([o.default.dynamicAnchors,o.default.dynamicAnchors]);let h=n._`${d}, ${r.object(...f)}`;return u!==n.nil?n._`${c}.call(${u}, ${h})`:n._`${c}(${h})`},t.usePattern=function({gen:e,it:{opts:t}},r){let i=t.unicodeRegExp?"u":"";return e.scopeValue("pattern",{key:r,ref:RegExp(r,i),code:n._`new RegExp(${r}, ${i})`})},t.validateArray=function(e){let{gen:t,data:r,keyword:o,it:a}=e,s=t.name("valid");if(a.allErrors){let e=t.let("valid",!0);return l((()=>t.assign(e,!1))),e}return t.var(s,!0),l((()=>t.break())),s;function l(a){let l=t.const("len",n._`${r}.length`);t.forRange("i",0,l,(r=>{e.subschema({keyword:o,dataProp:r,dataPropType:i.Type.Num},s),t.if(n.not(s),a)}))}},t.validateUnion=function(e){let{gen:t,schema:r,keyword:o,it:a}=e;if(!Array.isArray(r))throw Error("ajv implementation error");if(r.some((e=>i.alwaysValidSchema(a,e)))&&!a.opts.unevaluated)return;let s=t.let("valid",!1),l=t.name("_valid");t.block((()=>r.forEach(((r,i)=>{let a=e.subschema({keyword:o,schemaProp:i,compositeRule:!0},l);t.assign(s,n._`${s} || ${l}`),e.mergeValidEvaluated(a,l)||t.if(n.not(s))})))),e.result(s,(()=>e.reset()),(()=>e.error(!0)))}},5060:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={keyword:"id",code(){throw Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}}},8223:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(5060),i=r(4028),o=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",n.default,i.default];t.default=o},4028:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.callRef=t.getValidate=void 0;let n=r(4143),i=r(8619),o=r(4475),a=r(5018),s=r(7805),l=r(6124);function c(e,t){let{gen:r}=e;return t.validate?r.scopeValue("validate",{ref:t.validate}):o._`${r.scopeValue("wrapper",{ref:t})}.validate`}function u(e,t,r,n){let{gen:s,it:c}=e,{allErrors:u,schemaEnv:p,opts:d}=c,f=d.passContext?a.default.this:o.nil;function h(e){let t=o._`${e}.errors`;s.assign(a.default.vErrors,o._`${a.default.vErrors} === null ? ${t} : ${a.default.vErrors}.concat(${t})`),s.assign(a.default.errors,o._`${a.default.vErrors}.length`)}function m(e){var t;if(!c.opts.unevaluated)return;let n=null===(t=null==r?void 0:r.validate)||void 0===t?void 0:t.evaluated;if(!0!==c.props)if(n&&!n.dynamicProps)void 0!==n.props&&(c.props=l.mergeEvaluated.props(s,n.props,c.props));else{let t=s.var("props",o._`${e}.evaluated.props`);c.props=l.mergeEvaluated.props(s,t,c.props,o.Name)}if(!0!==c.items)if(n&&!n.dynamicItems)void 0!==n.items&&(c.items=l.mergeEvaluated.items(s,n.items,c.items));else{let t=s.var("items",o._`${e}.evaluated.items`);c.items=l.mergeEvaluated.items(s,t,c.items,o.Name)}}n?function(){if(!p.$async)throw Error("async schema referenced by sync schema");let r=s.let("valid");s.try((()=>{s.code(o._`await ${i.callValidateCode(e,t,f)}`),m(t),u||s.assign(r,!0)}),(e=>{s.if(o._`!(${e} instanceof ${c.ValidationError})`,(()=>s.throw(e))),h(e),u||s.assign(r,!1)})),e.ok(r)}():function(){let r=s.name("visitedNodes");s.code(o._`const ${r} = visitedNodesForRef.get(${t}) || new Set()`),s.if(o._`!${r}.has(${e.data})`,(()=>{s.code(o._`visitedNodesForRef.set(${t}, ${r})`),s.code(o._`const dataNode = ${e.data}`),s.code(o._`${r}.add(dataNode)`);let n=e.result(i.callValidateCode(e,t,f),(()=>m(t)),(()=>h(t)));return s.code(o._`${r}.delete(dataNode)`),n}))}()}t.getValidate=c,t.callRef=u,t.default={keyword:"$ref",schemaType:"string",code(e){let{gen:t,schema:r,it:i}=e,{baseId:a,schemaEnv:l,validateName:p,opts:d,self:f}=i,{root:h}=l;if(("#"===r||"#/"===r)&&a===h.baseId)return function(){if(l===h)return u(e,p,l,l.$async);let r=t.scopeValue("root",{ref:h});return u(e,o._`${r}.validate`,h,h.$async)}();let m=s.resolveRef.call(f,h,a,r);if(void 0===m)throw new n.default(a,r);return m instanceof s.SchemaEnv?function(t){let r=c(e,t);u(e,r,t,t.$async)}(m):function(n){let i=t.scopeValue("schema",!0===d.code.source?{ref:n,code:o.stringify(n)}:{ref:n}),a=t.name("valid"),s=e.subschema({schema:n,dataTypes:[],schemaPath:o.nil,topSchemaRef:i,errSchemaPath:r},a);e.mergeEvaluated(s),e.ok(a)}(m)}}},5522:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6545);t.default={keyword:"discriminator",type:"object",schemaType:"object",error:{message:({params:{discrError:e,tagName:t}})=>e===i.DiscrError.Tag?`tag "${t}" must be string`:`value of tag "${t}" must be in oneOf`,params:({params:{discrError:e,tag:t,tagName:r}})=>n._`{error: ${e}, tag: ${r}, tagValue: ${t}}`},code(e){let{gen:t,data:r,schema:o,parentSchema:a,it:s}=e,{oneOf:l}=a;if(!s.opts.discriminator)throw Error("discriminator: requires discriminator option");let c=o.propertyName;if("string"!=typeof c)throw Error("discriminator: requires propertyName");if(!l)throw Error("discriminator: requires oneOf keyword");let u=t.let("valid",!1),p=t.const("tag",n._`${r}${n.getProperty(c)}`);function d(r){let i=t.name("valid"),o=e.subschema({keyword:"oneOf",schemaProp:r},i);return e.mergeEvaluated(o,n.Name),i}function f(e){return e.hasOwnProperty("$ref")}t.if(n._`typeof ${p} == "string"`,(()=>function(){let r=function(){var e;let t={},r=i(a),n=!0;for(let t=0;te.error(!1,{discrError:i.DiscrError.Tag,tag:p,tagName:c}))),e.ok(u)}}},6545:function(e,t){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.DiscrError=void 0,(r=t.DiscrError||(t.DiscrError={})).Tag="tag",r.Mapping="mapping"},6479:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8223),i=r(3799),o=r(9616),a=r(3815),s=r(4826),l=[n.default,i.default,o.default(),a.default,s.metadataVocabulary,s.contentVocabulary];t.default=l},157:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:"format",type:["number","string"],schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>n.str`must match format "${e}"`,params:({schemaCode:e})=>n._`{format: ${e}}`},code(e,t){let{gen:r,data:i,$data:o,schema:a,schemaCode:s,it:l}=e,{opts:c,errSchemaPath:u,schemaEnv:p,self:d}=l;c.validateFormats&&(o?function(){let o=r.scopeValue("formats",{ref:d.formats,code:c.code.formats}),a=r.const("fDef",n._`${o}[${s}]`),l=r.let("fType"),u=r.let("format");r.if(n._`typeof ${a} == "object" && !(${a} instanceof RegExp)`,(()=>r.assign(l,n._`${a}.type || "string"`).assign(u,n._`${a}.validate`)),(()=>r.assign(l,n._`"string"`).assign(u,a))),e.fail$data(n.or(!1===c.strictSchema?n.nil:n._`${s} && !${u}`,function(){let e=p.$async?n._`(${a}.async ? await ${u}(${i}) : ${u}(${i}))`:n._`${u}(${i})`,r=n._`(typeof ${u} == "function" ? ${e} : ${u}.test(${i}))`;return n._`${u} && ${u} !== true && ${l} === ${t} && !${r}`}()))}():function(){let o=d.formats[a];if(!o)return void function(){if(!1!==c.strictSchema)throw Error(e());function e(){return`unknown format "${a}" ignored in schema at path "${u}"`}d.logger.warn(e())}();if(!0===o)return;let[s,l,f]=function(e){let t=e instanceof RegExp?n.regexpCode(e):c.code.formats?n._`${c.code.formats}${n.getProperty(a)}`:void 0,i=r.scopeValue("formats",{key:a,ref:e,code:t});return"object"!=typeof e||e instanceof RegExp?["string",e,i]:[e.type||"string",e.validate,n._`${i}.validate`]}(o);s===t&&e.pass(function(){if("object"==typeof o&&!(o instanceof RegExp)&&o.async){if(!p.$async)throw Error("async format in sync schema");return n._`await ${f}(${i})`}return"function"==typeof l?n._`${f}(${i})`:n._`${f}.test(${i})`}())}())}}},3815:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=[r(157).default];t.default=n},4826:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.contentVocabulary=t.metadataVocabulary=void 0,t.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"],t.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]},7535:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(412);t.default={keyword:"const",$data:!0,error:{message:"must be equal to constant",params:({schemaCode:e})=>n._`{allowedValue: ${e}}`},code(e){let{gen:t,data:r,$data:a,schemaCode:s,schema:l}=e;a||l&&"object"==typeof l?e.fail$data(n._`!${i.useFunc(t,o.default)}(${r}, ${s})`):e.fail(n._`${l} !== ${r}`)}}},4147:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(412);t.default={keyword:"enum",schemaType:"array",$data:!0,error:{message:"must be equal to one of the allowed values",params:({schemaCode:e})=>n._`{allowedValues: ${e}}`},code(e){let{gen:t,data:r,$data:a,schema:s,schemaCode:l,it:c}=e;if(!a&&0===s.length)throw Error("enum must have non-empty array");let u,p=s.length>=c.opts.loopEnum,d=i.useFunc(t,o.default);if(p||a)u=t.let("valid"),e.block$data(u,(function(){t.assign(u,!1),t.forOf("v",l,(e=>t.if(n._`${d}(${r}, ${e})`,(()=>t.assign(u,!0).break()))))}));else{if(!Array.isArray(s))throw Error("ajv implementation error");let e=t.const("vSchema",l);u=n.or(...s.map(((t,i)=>function(e,t){let i=s[t];return"object"==typeof i&&null!==i?n._`${d}(${r}, ${e}[${t}])`:n._`${r} === ${i}`}(e,i))))}e.pass(u)}}},3799:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(9640),i=r(7692),o=r(3765),a=r(8582),s=r(6711),l=r(7835),c=r(8950),u=r(7326),p=r(7535),d=r(4147),f=[n.default,i.default,o.default,a.default,s.default,l.default,c.default,u.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},p.default,d.default];t.default=f},8950:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxItems"===e?"more":"fewer"} than ${t} items`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e,o="maxItems"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`${r}.length ${o} ${i}`)}}},3765:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(5872);t.default={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxLength"===e?"more":"fewer"} than ${t} characters`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:a,it:s}=e,l="maxLength"===t?n.operators.GT:n.operators.LT,c=!1===s.opts.unicode?n._`${r}.length`:n._`${i.useFunc(e.gen,o.default)}(${r})`;e.fail$data(n._`${c} ${l} ${a}`)}}},9640:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=n.operators,o={maximum:{okStr:"<=",ok:i.LTE,fail:i.GT},minimum:{okStr:">=",ok:i.GTE,fail:i.LT},exclusiveMaximum:{okStr:"<",ok:i.LT,fail:i.GTE},exclusiveMinimum:{okStr:">",ok:i.GT,fail:i.LTE}},a={keyword:Object.keys(o),type:"number",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must be ${o[e].okStr} ${t}`,params:({keyword:e,schemaCode:t})=>n._`{comparison: ${o[e].okStr}, limit: ${t}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e;e.fail$data(n._`${r} ${o[t].fail} ${i} || isNaN(${r})`)}};t.default=a},6711:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxProperties"===e?"more":"fewer"} than ${t} items`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e,o="maxProperties"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`Object.keys(${r}).length ${o} ${i}`)}}},7692:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:"multipleOf",type:"number",schemaType:"number",$data:!0,error:{message:({schemaCode:e})=>n.str`must be multiple of ${e}`,params:({schemaCode:e})=>n._`{multipleOf: ${e}}`},code(e){let{gen:t,data:r,schemaCode:i,it:o}=e,a=o.opts.multipleOfPrecision,s=t.let("res"),l=a?n._`Math.abs(Math.round(${s}) - ${s}) > 1e-${a}`:n._`${s} !== parseInt(${s})`;e.fail$data(n._`(${i} === 0 || (${s} = ${r}/${i}, ${l}))`)}}},8582:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475);t.default={keyword:"pattern",type:"string",schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>i.str`must match pattern "${e}"`,params:({schemaCode:e})=>i._`{pattern: ${e}}`},code(e){let{data:t,$data:r,schema:o,schemaCode:a,it:s}=e,l=s.opts.unicodeRegExp?"u":"",c=r?i._`(new RegExp(${a}, ${l}))`:n.usePattern(e,o);e.fail$data(i._`!${c}.test(${t})`)}}},7835:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(6124);t.default={keyword:"required",type:"object",schemaType:"array",$data:!0,error:{message:({params:{missingProperty:e}})=>i.str`must have required property '${e}'`,params:({params:{missingProperty:e}})=>i._`{missingProperty: ${e}}`},code(e){let{gen:t,schema:r,schemaCode:a,data:s,$data:l,it:c}=e,{opts:u}=c;if(!l&&0===r.length)return;let p=r.length>=u.loopRequired;if(c.allErrors?function(){if(p||l)e.block$data(i.nil,d);else for(let t of r)n.checkReportMissingProp(e,t)}():function(){let o=t.let("missing");if(p||l){let r=t.let("valid",!0);e.block$data(r,(()=>{var l,c;return l=o,c=r,e.setParams({missingProperty:l}),void t.forOf(l,a,(()=>{t.assign(c,n.propertyInData(t,s,l,u.ownProperties)),t.if(i.not(c),(()=>{e.error(),t.break()}))}),i.nil)})),e.ok(r)}else t.if(n.checkMissingProp(e,r,o)),n.reportMissingProp(e,o),t.else()}(),u.strictRequired){let t=e.parentSchema.properties,{definedProperties:n}=e.it;for(let e of r)if(void 0===(null==t?void 0:t[e])&&!n.has(e)){let t=`required property "${e}" is not defined at "${c.schemaEnv.baseId+c.errSchemaPath}" (strictRequired)`;o.checkStrictMode(c,t,c.opts.strictRequired)}}function d(){t.forOf("prop",a,(r=>{e.setParams({missingProperty:r}),t.if(n.noPropertyInData(t,s,r,u.ownProperties),(()=>e.error()))}))}}}},7326:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(7927),i=r(4475),o=r(6124),a=r(412);t.default={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:!0,error:{message:({params:{i:e,j:t}})=>i.str`must NOT have duplicate items (items ## ${t} and ${e} are identical)`,params:({params:{i:e,j:t}})=>i._`{i: ${e}, j: ${t}}`},code(e){let{gen:t,data:r,$data:s,schema:l,parentSchema:c,schemaCode:u,it:p}=e;if(!s&&!l)return;let d=t.let("valid"),f=c.items?n.getSchemaTypes(c.items):[];function h(o,a){let s=t.name("item"),l=n.checkDataTypes(f,s,p.opts.strictNumbers,n.DataType.Wrong),c=t.const("indices",i._`{}`);t.for(i._`;${o}--;`,(()=>{t.let(s,i._`${r}[${o}]`),t.if(l,i._`continue`),f.length>1&&t.if(i._`typeof ${s} == "string"`,i._`${s} += "_"`),t.if(i._`typeof ${c}[${s}] == "number"`,(()=>{t.assign(a,i._`${c}[${s}]`),e.error(),t.assign(d,!1).break()})).code(i._`${c}[${s}] = ${o}`)}))}function m(n,s){let l=o.useFunc(t,a.default),c=t.name("outer");t.label(c).for(i._`;${n}--;`,(()=>t.for(i._`${s} = ${n}; ${s}--;`,(()=>t.if(i._`${l}(${r}[${n}], ${r}[${s}])`,(()=>{e.error(),t.assign(d,!1).break(c)}))))))}e.block$data(d,(function(){let n=t.let("i",i._`${r}.length`),o=t.let("j");e.setParams({i:n,j:o}),t.assign(d,!0),t.if(i._`${n} > 1`,(()=>(f.length>0&&!f.some((e=>"object"===e||"array"===e))?h:m)(n,o)))}),i._`${u} === false`),e.ok(d)}}},4029:function(e){"use strict";var t=e.exports=function(e,r,n){"function"==typeof r&&(n=r,r={}),function e(r,n,i,o,a,s,l,c,u,p){if(o&&"object"==typeof o&&!Array.isArray(o)){for(var d in n(o,a,s,l,c,u,p),o){var f=o[d];if(Array.isArray(f)){if(d in t.arrayKeywords)for(var h=0;h0;)if(a=s.pop()+(a?`-${a}`:""),!o||!o[a]||d(o[a],e,r))return a;if(!o[a=m.refBaseName(n)+(a?`_${a}`:"")]||d(o[a],e,r))return a;let c=a,u=2;for(;o[a]&&!d(o[a],e,r);)a=`${c}-${u}`,u++;return o[a]||r.report({message:`Two schemas are referenced with the same name but different content. Renamed ${c} to ${a}.`,location:r.location,forceSeverity:"warn"}),a}(r,t,n);return l[t][i]=r.node,e===h.OasMajorVersion.Version3?`#/components/${t}/${i}`:`#/${t}/${i}`}function d(e,t,r){var n;return!(!m.isRef(e)||(null===(n=r.resolve(e).location)||void 0===n?void 0:n.absolutePointer)!==t.location.absolutePointer)||a(e,t.node)}return e===h.OasMajorVersion.Version3&&(c.DiscriminatorMapping={leave(r,n){for(let i of Object.keys(r)){let o=r[i],a=n.resolve({$ref:o});if(!a.location||void 0===a.node)return void y.reportUnresolvedRef(a,n.report,n.location.child(i));let s=_("Schema",e);t?p(s,a,n):r[i]=p(s,a,n)}}}),c}(A,k,O,t,I,E)},...j],C);return f.walkDocument({document:t,rootType:C.DefinitionRoot,normalizedVisitors:N,resolvedRefMap:I,ctx:T}),{bundle:t,problems:T.problems.map((e=>r.addProblemToIgnore(e))),fileDependencies:o.getFiles(),rootType:C.DefinitionRoot,refTypes:T.refTypes,visitorsData:T.visitorsData}}))}function _(e,t){switch(t){case h.OasMajorVersion.Version3:switch(e){case"Schema":return"schemas";case"Parameter":return"parameters";case"Response":return"responses";case"Example":return"examples";case"RequestBody":return"requestBodies";case"Header":return"headers";case"SecuritySchema":return"securitySchemes";case"Link":return"links";case"Callback":return"callbacks";default:return null}case h.OasMajorVersion.Version2:switch(e){case"Schema":return"definitions";case"Parameter":return"parameters";case"Response":return"responses";default:return null}}}(n=i=t.OasVersion||(t.OasVersion={})).Version2="oas2",n.Version3_0="oas3_0",n.Version3_1="oas3_1",t.bundle=function(e){return o(this,void 0,void 0,(function*(){let{ref:t,doc:r,externalRefResolver:n=new s.BaseResolver(e.config.resolve),base:i=null}=e;if(!t&&!r)throw Error("Document or reference is required.\n");let o=void 0!==r?r:yield n.resolveDocument(i,t,!0);if(o instanceof Error)throw o;return k(Object.assign(Object.assign({document:o},e),{config:e.config.lint,externalRefResolver:n}))}))},t.bundleDocument=k,t.mapTypeToComponent=_},6877:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"error","info-contact":"error","info-license":"error","info-license-url":"error","tag-description":"error","tags-alphabetical":"error","parameter-description":"error","no-identical-paths":"error","no-ambiguous-paths":"error","no-path-trailing-slash":"error","path-segment-plural":"error","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"error","operation-2xx-response":"error","operation-4xx-response":"error",assertions:"error","operation-operationId":"error","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"error","operation-security-defined":"error","operation-singular-tag":"error","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"error","paths-kebab-case":"error","no-http-verbs-in-paths":"error","path-excludes-patterns":{severity:"error",patterns:[]},"request-mime-type":"error",spec:"error","no-invalid-schema-examples":"error","no-invalid-parameter-examples":"error","scalar-property-missing-example":"error"},oas3_0Rules:{"no-invalid-media-type-examples":"error","no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},6242:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultPlugin=t.builtInConfigs=void 0;let n=r(8057),i=r(6877),o=r(9016),a=r(226),s=r(7523),l=r(226),c=r(7523),u=r(1753),p=r(7060);t.builtInConfigs={recommended:n.default,minimal:o.default,all:i.default,"redocly-registry":{decorators:{"registry-dependencies":"on"}}},t.defaultPlugin={id:"",rules:{oas3:a.rules,oas2:s.rules},preprocessors:{oas3:l.preprocessors,oas2:c.preprocessors},decorators:{oas3:u.decorators,oas2:p.decorators},configs:t.builtInConfigs}},7040:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))},i=this&&this.__rest||function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]])}return r};Object.defineProperty(t,"__esModule",{value:!0}),t.resolvePreset=t.resolveLint=t.resolveApis=t.resolvePlugins=t.resolveConfig=void 0;let o=r(6470),a=r(6212),s=r(7468),l=r(4182),c=r(6242),u=r(2565),p=r(771),d=r(3777);function f(e,t=""){if(!e)return[];let r=require,n=new Map;return e.map((e=>{if(p.isString(e)&&s.isAbsoluteUrl(e))throw Error(a.red("We don't support remote plugins yet."));let i=p.isString(e)?r(o.resolve(o.dirname(t),e)):e,l=i.id;if("string"!=typeof l)throw Error(a.red(`Plugin must define \`id\` property in ${a.blue(e.toString())}.`));if(n.has(l)){let t=n.get(l);throw Error(a.red(`Plugin "id" must be unique. Plugin ${a.blue(e.toString())} uses id "${a.blue(l)}" already seen in ${a.blue(t)}`))}n.set(l,e.toString());let c=Object.assign(Object.assign({id:l},i.configs?{configs:i.configs}:{}),i.typeExtension?{typeExtension:i.typeExtension}:{});if(i.rules){if(!i.rules.oas3&&!i.rules.oas2)throw Error(`Plugin rules must have \`oas3\` or \`oas2\` rules "${e}.`);c.rules={},i.rules.oas3&&(c.rules.oas3=u.prefixRules(i.rules.oas3,l)),i.rules.oas2&&(c.rules.oas2=u.prefixRules(i.rules.oas2,l))}if(i.preprocessors){if(!i.preprocessors.oas3&&!i.preprocessors.oas2)throw Error(`Plugin \`preprocessors\` must have \`oas3\` or \`oas2\` preprocessors "${e}.`);c.preprocessors={},i.preprocessors.oas3&&(c.preprocessors.oas3=u.prefixRules(i.preprocessors.oas3,l)),i.preprocessors.oas2&&(c.preprocessors.oas2=u.prefixRules(i.preprocessors.oas2,l))}if(i.decorators){if(!i.decorators.oas3&&!i.decorators.oas2)throw Error(`Plugin \`decorators\` must have \`oas3\` or \`oas2\` decorators "${e}.`);c.decorators={},i.decorators.oas3&&(c.decorators.oas3=u.prefixRules(i.decorators.oas3,l)),i.decorators.oas2&&(c.decorators.oas2=u.prefixRules(i.decorators.oas2,l))}return c})).filter(p.notUndefined)}function h({rawConfig:e,configPath:t="",resolver:r}){var i,o;return n(this,void 0,void 0,(function*(){let{apis:n={},lint:a={}}=e,s={};for(let[e,l]of Object.entries(n||{})){if(null===(o=null===(i=l.lint)||void 0===i?void 0:i.extends)||void 0===o?void 0:o.some(p.isNotString))throw Error("Error configuration format not detected in extends value must contain strings");let n=y(a,l.lint),c=yield m({lintConfig:n,configPath:t,resolver:r});s[e]=Object.assign(Object.assign({},l),{lint:c})}return s}))}function m(e,t=[],r=[]){return n(this,void 0,void 0,(function*(){let a=yield function e({lintConfig:t,configPath:r="",resolver:a=new l.BaseResolver},d=[],h=[]){var m,y,v;return n(this,void 0,void 0,(function*(){if(d.includes(r))throw Error(`Circular dependency in config file: "${r}"`);let l=u.getUniquePlugins(f([...(null==t?void 0:t.plugins)||[],c.defaultPlugin],r)),b=null===(m=null==t?void 0:t.plugins)||void 0===m?void 0:m.filter(p.isString).map((e=>o.resolve(o.dirname(r),e))),x=s.isAbsoluteUrl(r)?r:r&&o.resolve(r),w=yield Promise.all((null===(y=null==t?void 0:t.extends)||void 0===y?void 0:y.map((t=>n(this,void 0,void 0,(function*(){if(!s.isAbsoluteUrl(t)&&!o.extname(t))return g(t,l);let i=s.isAbsoluteUrl(t)?t:s.isAbsoluteUrl(r)?new URL(t,r).href:o.resolve(o.dirname(r),t),c=yield function(e,t){return n(this,void 0,void 0,(function*(){try{let r=yield t.loadExternalRef(e),n=u.transformConfig(p.parseYaml(r.body));if(!n.lint)throw Error(`Lint configuration format not detected: "${e}"`);return n.lint}catch(t){throw Error(`Failed to load "${e}": ${t.message}`)}}))}(i,a);return yield e({lintConfig:c,configPath:i,resolver:a},[...d,x],h)})))))||[]),k=u.mergeExtends([...w,Object.assign(Object.assign({},t),{plugins:l,extends:void 0,extendPaths:[...d,x],pluginPaths:b})]),{plugins:_=[]}=k,O=i(k,["plugins"]);return Object.assign(Object.assign({},O),{extendPaths:null===(v=O.extendPaths)||void 0===v?void 0:v.filter((e=>e&&!s.isAbsoluteUrl(e))),plugins:u.getUniquePlugins(_),recommendedFallback:null==t?void 0:t.recommendedFallback,doNotResolveExamples:null==t?void 0:t.doNotResolveExamples})}))}(e,t,r);return Object.assign(Object.assign({},a),{rules:a.rules&&function(e){if(!e)return e;let t={},r=[];for(let[n,i]of Object.entries(e))if(n.startsWith("assert/")&&"object"==typeof i&&null!==i){let e=i;r.push(Object.assign(Object.assign({},e),{assertionId:n.replace("assert/","")}))}else t[n]=i;return r.length>0&&(t.assertions=r),t}(a.rules)})}))}function g(e,t){var r;let{pluginId:n,configName:i}=u.parsePresetName(e),o=t.find((e=>e.id===n));if(!o)throw Error(`Invalid config ${a.red(e)}: plugin ${n} is not included.`);let s=null===(r=o.configs)||void 0===r?void 0:r[i];if(!s)throw Error(n?`Invalid config ${a.red(e)}: plugin ${n} doesn't export config with name ${i}.`:`Invalid config ${a.red(e)}: there is no such built-in config.`);return s}function y(e,t){return Object.assign(Object.assign(Object.assign({},e),t),{rules:Object.assign(Object.assign({},null==e?void 0:e.rules),null==t?void 0:t.rules),oas2Rules:Object.assign(Object.assign({},null==e?void 0:e.oas2Rules),null==t?void 0:t.oas2Rules),oas3_0Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Rules),null==t?void 0:t.oas3_0Rules),oas3_1Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Rules),null==t?void 0:t.oas3_1Rules),preprocessors:Object.assign(Object.assign({},null==e?void 0:e.preprocessors),null==t?void 0:t.preprocessors),oas2Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas2Preprocessors),null==t?void 0:t.oas2Preprocessors),oas3_0Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Preprocessors),null==t?void 0:t.oas3_0Preprocessors),oas3_1Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Preprocessors),null==t?void 0:t.oas3_1Preprocessors),decorators:Object.assign(Object.assign({},null==e?void 0:e.decorators),null==t?void 0:t.decorators),oas2Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas2Decorators),null==t?void 0:t.oas2Decorators),oas3_0Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Decorators),null==t?void 0:t.oas3_0Decorators),oas3_1Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Decorators),null==t?void 0:t.oas3_1Decorators),recommendedFallback:!(null==t?void 0:t.extends)&&e.recommendedFallback})}t.resolveConfig=function(e,t){var r,i,o,a,s;return n(this,void 0,void 0,(function*(){if(null===(i=null===(r=e.lint)||void 0===r?void 0:r.extends)||void 0===i?void 0:i.some(p.isNotString))throw Error("Error configuration format not detected in extends value must contain strings");let n=new l.BaseResolver(u.getResolveConfig(e.resolve)),c=null!==(a=null===(o=null==e?void 0:e.lint)||void 0===o?void 0:o.extends)&&void 0!==a?a:["recommended"],f=!(null===(s=null==e?void 0:e.lint)||void 0===s?void 0:s.extends),g=Object.assign(Object.assign({},null==e?void 0:e.lint),{extends:c,recommendedFallback:f}),y=yield h({rawConfig:Object.assign(Object.assign({},e),{lint:g}),configPath:t,resolver:n}),v=yield m({lintConfig:g,configPath:t,resolver:n});return new d.Config(Object.assign(Object.assign({},e),{apis:y,lint:v}),t)}))},t.resolvePlugins=f,t.resolveApis=h,t.resolveLint=m,t.resolvePreset=g},3777:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Config=t.LintConfig=t.AVAILABLE_REGIONS=t.DOMAINS=t.DEFAULT_REGION=t.IGNORE_FILE=t.env=void 0;let n=r(5101),i=r(6470),o=r(5273),a=r(771),s=r(1510),l=r(2565);t.env="undefined"!=typeof process&&{}||{},t.IGNORE_FILE=".redocly.lint-ignore.yaml",t.DEFAULT_REGION="us",t.DOMAINS=function(){let e={us:"redocly.com",eu:"eu.redocly.com"},r=t.env.REDOCLY_DOMAIN;return(null==r?void 0:r.endsWith(".redocly.host"))&&(e[r.split(".")[0]]=r),"redoc.online"===r&&(e[r]=r),e}(),t.AVAILABLE_REGIONS=Object.keys(t.DOMAINS);class c{constructor(e,r){this.rawConfig=e,this.configFile=r,this.ignore={},this._usedRules=new Set,this._usedVersions=new Set,this.plugins=e.plugins||[],this.doNotResolveExamples=!!e.doNotResolveExamples,this.recommendedFallback=e.recommendedFallback||!1,this.rules={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.rules),e.oas2Rules),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.rules),e.oas3_0Rules),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.rules),e.oas3_1Rules)},this.preprocessors={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.preprocessors),e.oas2Preprocessors),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.preprocessors),e.oas3_0Preprocessors),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.preprocessors),e.oas3_1Preprocessors)},this.decorators={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.decorators),e.oas2Decorators),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.decorators),e.oas3_0Decorators),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.decorators),e.oas3_1Decorators)},this.extendPaths=e.extendPaths||[],this.pluginPaths=e.pluginPaths||[];let a=this.configFile?i.dirname(this.configFile):"undefined"!=typeof process&&process.cwd()||"",l=i.join(a,t.IGNORE_FILE);if(n.hasOwnProperty("existsSync")&&n.existsSync(l))for(let e of(this.ignore=o.parseYaml(n.readFileSync(l,"utf-8"))||{},Object.keys(this.ignore))){for(let t of(this.ignore[i.resolve(i.dirname(l),e)]=this.ignore[e],Object.keys(this.ignore[e])))this.ignore[e][t]=new Set(this.ignore[e][t]);delete this.ignore[e]}}saveIgnore(){let e=this.configFile?i.dirname(this.configFile):process.cwd(),r=i.join(e,t.IGNORE_FILE),s={};for(let t of Object.keys(this.ignore)){let r=s[a.slash(i.relative(e,t))]=this.ignore[t];for(let e of Object.keys(r))r[e]=Array.from(r[e])}n.writeFileSync(r,"# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n# See https://redoc.ly/docs/cli/ for more information.\n"+o.stringifyYaml(s))}addIgnore(e){let t=this.ignore,r=e.location[0];if(void 0===r.pointer)return;let n=t[r.source.absoluteRef]=t[r.source.absoluteRef]||{};(n[e.ruleId]=n[e.ruleId]||new Set).add(r.pointer)}addProblemToIgnore(e){let t=e.location[0];if(void 0===t.pointer)return e;let r=(this.ignore[t.source.absoluteRef]||{})[e.ruleId],n=r&&r.has(t.pointer);return n?Object.assign(Object.assign({},e),{ignored:n}):e}extendTypes(e,t){let r=e;for(let e of this.plugins)if(void 0!==e.typeExtension)switch(t){case s.OasVersion.Version3_0:case s.OasVersion.Version3_1:if(!e.typeExtension.oas3)continue;r=e.typeExtension.oas3(r,t);case s.OasVersion.Version2:if(!e.typeExtension.oas2)continue;r=e.typeExtension.oas2(r,t);default:throw Error("Not implemented")}return r}getRuleSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.rules[t][e]||"off";return"string"==typeof r?{severity:r}:Object.assign({severity:"error"},r)}getPreprocessorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.preprocessors[t][e]||"off";return"string"==typeof r?{severity:"on"===r?"error":r}:Object.assign({severity:"error"},r)}getDecoratorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.decorators[t][e]||"off";return"string"==typeof r?{severity:"on"===r?"error":r}:Object.assign({severity:"error"},r)}getUnusedRules(){let e=[],t=[],r=[];for(let n of Array.from(this._usedVersions))e.push(...Object.keys(this.rules[n]).filter((e=>!this._usedRules.has(e)))),t.push(...Object.keys(this.decorators[n]).filter((e=>!this._usedRules.has(e)))),r.push(...Object.keys(this.preprocessors[n]).filter((e=>!this._usedRules.has(e))));return{rules:e,preprocessors:r,decorators:t}}getRulesForOasVersion(e){switch(e){case s.OasMajorVersion.Version3:let e=[];return this.plugins.forEach((t=>{var r;return(null===(r=t.preprocessors)||void 0===r?void 0:r.oas3)&&e.push(t.preprocessors.oas3)})),this.plugins.forEach((t=>{var r;return(null===(r=t.rules)||void 0===r?void 0:r.oas3)&&e.push(t.rules.oas3)})),this.plugins.forEach((t=>{var r;return(null===(r=t.decorators)||void 0===r?void 0:r.oas3)&&e.push(t.decorators.oas3)})),e;case s.OasMajorVersion.Version2:let t=[];return this.plugins.forEach((e=>{var r;return(null===(r=e.preprocessors)||void 0===r?void 0:r.oas2)&&t.push(e.preprocessors.oas2)})),this.plugins.forEach((e=>{var r;return(null===(r=e.rules)||void 0===r?void 0:r.oas2)&&t.push(e.rules.oas2)})),this.plugins.forEach((e=>{var r;return(null===(r=e.decorators)||void 0===r?void 0:r.oas2)&&t.push(e.decorators.oas2)})),t}}skipRules(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.rules[e][t]&&(this.rules[e][t]="off")}skipPreprocessors(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.preprocessors[e][t]&&(this.preprocessors[e][t]="off")}skipDecorators(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.decorators[e][t]&&(this.decorators[e][t]="off")}}t.LintConfig=c,t.Config=class{constructor(e,t){this.rawConfig=e,this.configFile=t,this.apis=e.apis||{},this.lint=new c(e.lint||{},t),this["features.openapi"]=e["features.openapi"]||{},this["features.mockServer"]=e["features.mockServer"]||{},this.resolve=l.getResolveConfig(null==e?void 0:e.resolve),this.region=e.region,this.organization=e.organization}}},8698:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),i=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),i(r(3777),t),i(r(3865),t),i(r(5030),t),i(r(6242),t),i(r(9129),t),i(r(2565),t),i(r(7040),t)},9129:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.getConfig=t.findConfig=t.CONFIG_FILE_NAMES=t.loadConfig=void 0;let i=r(5101),o=r(6470),a=r(1094),s=r(771),l=r(3777),c=r(2565),u=r(7040);function p(e){if(!i.hasOwnProperty("existsSync"))return;let r=t.CONFIG_FILE_NAMES.map((t=>e?o.resolve(e,t):t)).filter(i.existsSync);if(r.length>1)throw Error(`\n Multiple configuration files are not allowed. \n Found the following files: ${r.join(", ")}. \n Please use 'redocly.yaml' instead.\n `);return r[0]}function d(e=p()){return n(this,void 0,void 0,(function*(){if(!e)return{};try{let t=(yield s.loadYaml(e))||{};return c.transformConfig(t)}catch(t){throw Error(`Error parsing config file at '${e}': ${t.message}`)}}))}t.loadConfig=function(e=p(),t,r){return n(this,void 0,void 0,(function*(){let i=yield d(e);return"function"==typeof r&&(yield r(i)),yield function({rawConfig:e,customExtends:t,configPath:r}){var i;return n(this,void 0,void 0,(function*(){void 0!==t?(e.lint=e.lint||{},e.lint.extends=t):s.isEmptyObject(e);let n=new a.RedoclyClient,o=yield n.getTokens();if(o.length)for(let t of(e.resolve||(e.resolve={}),e.resolve.http||(e.resolve.http={}),e.resolve.http.headers=[...null!==(i=e.resolve.http.headers)&&void 0!==i?i:[]],o)){let r=l.DOMAINS[t.region];e.resolve.http.headers.push({matches:`https://api.${r}/registry/**`,name:"Authorization",envVariable:void 0,value:t.token},..."us"===t.region?[{matches:"https://api.redoc.ly/registry/**",name:"Authorization",envVariable:void 0,value:t.token}]:[])}return u.resolveConfig(e,r)}))}({rawConfig:i,customExtends:t,configPath:e})}))},t.CONFIG_FILE_NAMES=["redocly.yaml","redocly.yml",".redocly.yaml",".redocly.yml"],t.findConfig=p,t.getConfig=d},9016:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"off","info-license-url":"off","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"warn","no-identical-paths":"warn","no-ambiguous-paths":"warn","path-declaration-must-exist":"warn","path-not-include-query":"warn","path-parameters-defined":"warn","operation-description":"off","operation-2xx-response":"warn","operation-4xx-response":"off",assertions:"warn","operation-operationId":"warn","operation-summary":"warn","operation-operationId-unique":"warn","operation-parameters-unique":"warn","operation-tag-defined":"off","operation-security-defined":"warn","operation-operationId-url-safe":"warn","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"warn","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"}}},8057:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"warn","info-license-url":"warn","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"error","no-identical-paths":"error","no-ambiguous-paths":"warn","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"off","operation-2xx-response":"warn",assertions:"warn","operation-4xx-response":"warn","operation-operationId":"warn","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"off","operation-security-defined":"error","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},5030:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.initRules=void 0;let n=r(771);t.initRules=function(e,t,r,i){return e.flatMap((e=>Object.keys(e).map((n=>{let o=e[n],a="rules"===r?t.getRuleSettings(n,i):"preprocessors"===r?t.getPreprocessorSettings(n,i):t.getDecoratorSettings(n,i);if("off"===a.severity)return;let s=o(a);return Array.isArray(s)?s.map((e=>({severity:a.severity,ruleId:n,visitor:e}))):{severity:a.severity,ruleId:n,visitor:s}})))).flatMap((e=>e)).filter(n.notUndefined)}},3865:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},2565:function(e,t,r){"use strict";var n=this&&this.__rest||function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]])}return r};Object.defineProperty(t,"__esModule",{value:!0}),t.getUniquePlugins=t.getResolveConfig=t.transformConfig=t.getMergedConfig=t.mergeExtends=t.prefixRules=t.transformApiDefinitionsToApis=t.parsePresetName=void 0;let i=r(6212),o=r(771),a=r(3777);function s(e={}){let t={};for(let[r,n]of Object.entries(e))t[r]={root:n};return t}t.parsePresetName=function(e){if(e.indexOf("/")>-1){let[t,r]=e.split("/");return{pluginId:t,configName:r}}return{pluginId:"",configName:e}},t.transformApiDefinitionsToApis=s,t.prefixRules=function(e,t){if(!t)return e;let r={};for(let n of Object.keys(e))r[`${t}/${n}`]=e[n];return r},t.mergeExtends=function(e){let t={rules:{},oas2Rules:{},oas3_0Rules:{},oas3_1Rules:{},preprocessors:{},oas2Preprocessors:{},oas3_0Preprocessors:{},oas3_1Preprocessors:{},decorators:{},oas2Decorators:{},oas3_0Decorators:{},oas3_1Decorators:{},plugins:[],pluginPaths:[],extendPaths:[]};for(let r of e){if(r.extends)throw Error(`\`extends\` is not supported in shared configs yet: ${JSON.stringify(r,null,2)}.`);Object.assign(t.rules,r.rules),Object.assign(t.oas2Rules,r.oas2Rules),o.assignExisting(t.oas2Rules,r.rules||{}),Object.assign(t.oas3_0Rules,r.oas3_0Rules),o.assignExisting(t.oas3_0Rules,r.rules||{}),Object.assign(t.oas3_1Rules,r.oas3_1Rules),o.assignExisting(t.oas3_1Rules,r.rules||{}),Object.assign(t.preprocessors,r.preprocessors),Object.assign(t.oas2Preprocessors,r.oas2Preprocessors),o.assignExisting(t.oas2Preprocessors,r.preprocessors||{}),Object.assign(t.oas3_0Preprocessors,r.oas3_0Preprocessors),o.assignExisting(t.oas3_0Preprocessors,r.preprocessors||{}),Object.assign(t.oas3_1Preprocessors,r.oas3_1Preprocessors),o.assignExisting(t.oas3_1Preprocessors,r.preprocessors||{}),Object.assign(t.decorators,r.decorators),Object.assign(t.oas2Decorators,r.oas2Decorators),o.assignExisting(t.oas2Decorators,r.decorators||{}),Object.assign(t.oas3_0Decorators,r.oas3_0Decorators),o.assignExisting(t.oas3_0Decorators,r.decorators||{}),Object.assign(t.oas3_1Decorators,r.oas3_1Decorators),o.assignExisting(t.oas3_1Decorators,r.decorators||{}),t.plugins.push(...r.plugins||[]),t.pluginPaths.push(...r.pluginPaths||[]),t.extendPaths.push(...new Set(r.extendPaths))}return t},t.getMergedConfig=function(e,t){var r,n,i,o,s,l;let c=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.extendPaths})),null===(n=null===(r=e.rawConfig)||void 0===r?void 0:r.lint)||void 0===n?void 0:n.extendPaths].flat().filter(Boolean),u=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.pluginPaths})),null===(o=null===(i=e.rawConfig)||void 0===i?void 0:i.lint)||void 0===o?void 0:o.pluginPaths].flat().filter(Boolean);return t?new a.Config(Object.assign(Object.assign({},e.rawConfig),{lint:Object.assign(Object.assign({},e.apis[t]?e.apis[t].lint:e.rawConfig.lint),{extendPaths:c,pluginPaths:u}),"features.openapi":Object.assign(Object.assign({},e["features.openapi"]),null===(s=e.apis[t])||void 0===s?void 0:s["features.openapi"]),"features.mockServer":Object.assign(Object.assign({},e["features.mockServer"]),null===(l=e.apis[t])||void 0===l?void 0:l["features.mockServer"])}),e.configFile):e},t.transformConfig=function(e){if(e.apis&&e.apiDefinitions)throw Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");if(e["features.openapi"]&&e.referenceDocs)throw Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");let t=e,{apiDefinitions:r,referenceDocs:o}=t,a=n(t,["apiDefinitions","referenceDocs"]);return r&&process.stderr.write(`The ${i.yellow("apiDefinitions")} field is deprecated. Use ${i.green("apis")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),o&&process.stderr.write(`The ${i.yellow("referenceDocs")} field is deprecated. Use ${i.green("features.openapi")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),Object.assign({"features.openapi":o,apis:s(r)},a)},t.getResolveConfig=function(e){var t,r;return{http:{headers:null!==(r=null===(t=null==e?void 0:e.http)||void 0===t?void 0:t.headers)&&void 0!==r?r:[],customFetch:void 0}}},t.getUniquePlugins=function(e){let t=new Set,r=[];for(let n of e)t.has(n.id)?n.id&&process.stderr.write(`Duplicate plugin id "${i.yellow(n.id)}".\n`):(r.push(n),t.add(n.id));return r}},1988:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkIfMatchByStrategy=t.filter=void 0;let n=r(7468),i=r(771);function o(e){return Array.isArray(e)?e:[e]}t.filter=function(e,t,r){let{parent:o,key:a}=t,s=!1;if(Array.isArray(e))for(let i=0;ie.includes(t))):"all"===r&&t.every((t=>e.includes(t)))):e===t)}},9244:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterIn=void 0;let n=r(1988);t.FilterIn=({property:e,value:t,matchStrategy:r})=>{let i=r||"any",o=r=>(null==r?void 0:r[e])&&!n.checkIfMatchByStrategy(null==r?void 0:r[e],t,i);return{any:{enter(e,t){n.filter(e,t,o)}}}}},8623:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterOut=void 0;let n=r(1988);t.FilterOut=({property:e,value:t,matchStrategy:r})=>{let i=r||"any",o=r=>n.checkIfMatchByStrategy(null==r?void 0:r[e],t,i);return{any:{enter(e,t){n.filter(e,t,o)}}}}},4555:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescriptionOverride=void 0;let n=r(771);t.InfoDescriptionOverride=({filePath:e})=>({Info:{leave(t,{report:r,location:i}){if(!e)throw Error('Parameter "filePath" is not provided for "info-description-override" rule');try{t.description=n.readFileAsStringSync(e)}catch(e){r({message:`Failed to read markdown override file for "info.description".\n${e.message}`,location:i.child("description")})}}}})},7802:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescriptionOverride=void 0;let n=r(771);t.OperationDescriptionOverride=({operationIds:e})=>({Operation:{leave(t,{report:r,location:i}){if(!t.operationId)return;if(!e)throw Error('Parameter "operationIds" is not provided for "operation-description-override" rule');let o=t.operationId;if(e[o])try{t.description=n.readFileAsStringSync(e[o])}catch(e){r({message:`Failed to read markdown override file for operation "${o}".\n${e.message}`,location:i.child("operationId").key()})}}}})},2287:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryDependencies=void 0;let n=r(1094);t.RegistryDependencies=()=>{let e=new Set;return{DefinitionRoot:{leave(t,r){r.getVisitorData().links=Array.from(e)}},ref(t){if(t.$ref){let r=t.$ref.split("#/")[0];n.isRedoclyRegistryURL(r)&&e.add(r)}}}}},5830:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveXInternal=void 0;let n=r(771),i=r(7468);t.RemoveXInternal=({internalFlagProperty:e})=>{let t=e||"x-internal";return{any:{enter(e,r){!function(e,r){var o,a,s,l;let{parent:c,key:u}=r,p=!1;if(Array.isArray(e))for(let n=0;n({Tag:{leave(t,{report:r}){if(!e)throw Error('Parameter "tagNames" is not provided for "tag-description-override" rule');if(e[t.name])try{t.description=n.readFileAsStringSync(e[t.name])}catch(e){r({message:`Failed to read markdown override file for tag "${t.name}".\n${e.message}`})}}}})},7060:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;let n=r(2287),i=r(7802),o=r(423),a=r(4555),s=r(5830),l=r(9244),c=r(8623);t.decorators={"registry-dependencies":n.RegistryDependencies,"operation-description-override":i.OperationDescriptionOverride,"tag-description-override":o.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},1753:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;let n=r(2287),i=r(7802),o=r(423),a=r(4555),s=r(5830),l=r(9244),c=r(8623);t.decorators={"registry-dependencies":n.RegistryDependencies,"operation-description-override":i.OperationDescriptionOverride,"tag-description-override":o.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},5273:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.stringifyYaml=t.parseYaml=void 0;let n=r(3320),i=n.JSON_SCHEMA.extend({implicit:[n.types.merge],explicit:[n.types.binary,n.types.omap,n.types.pairs,n.types.set]});t.parseYaml=(e,t)=>n.load(e,Object.assign({schema:i},t)),t.stringifyYaml=(e,t)=>n.dump(e,t)},1510:function(e,t){"use strict";var r,n,i,o;Object.defineProperty(t,"__esModule",{value:!0}),t.openAPIMajor=t.detectOpenAPI=t.OasMajorVersion=t.OasVersion=void 0,(i=r=t.OasVersion||(t.OasVersion={})).Version2="oas2",i.Version3_0="oas3_0",i.Version3_1="oas3_1",(o=n=t.OasMajorVersion||(t.OasMajorVersion={})).Version2="oas2",o.Version3="oas3",t.detectOpenAPI=function(e){if("object"!=typeof e)throw Error("Document must be JSON object, got "+typeof e);if(!e.openapi&&!e.swagger)throw Error("This doesn’t look like an OpenAPI document.\n");if(e.openapi&&"string"!=typeof e.openapi)throw Error(`Invalid OpenAPI version: should be a string but got "${typeof e.openapi}"`);if(e.openapi&&e.openapi.startsWith("3.0"))return r.Version3_0;if(e.openapi&&e.openapi.startsWith("3.1"))return r.Version3_1;if(e.swagger&&"2.0"===e.swagger)return r.Version2;throw Error(`Unsupported OpenAPI Version: ${e.openapi||e.swagger}`)},t.openAPIMajor=function(e){return e===r.Version2?n.Version2:n.Version3}},1094:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isRedoclyRegistryURL=t.RedoclyClient=void 0;let i=r(2116),o=r(6470),a=r(6918),s=r(8836),l=r(1390),c=r(3777),u=r(771),p=".redocly-config.json";t.RedoclyClient=class{constructor(e){this.accessTokens={},this.region=this.loadRegion(e),this.loadTokens(),this.domain=e?c.DOMAINS[e]:c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION],c.env.REDOCLY_DOMAIN=this.domain,this.registryApi=new l.RegistryApi(this.accessTokens,this.region)}loadRegion(e){if(e&&!c.DOMAINS[e])throw Error(`Invalid argument: region in config file.\nGiven: ${s.green(e)}, choices: "us", "eu".`);return c.env.REDOCLY_DOMAIN?c.AVAILABLE_REGIONS.find((e=>c.DOMAINS[e]===c.env.REDOCLY_DOMAIN))||c.DEFAULT_REGION:e||c.DEFAULT_REGION}getRegion(){return this.region}hasTokens(){return u.isNotEmptyObject(this.accessTokens)}hasToken(){return!!this.accessTokens[this.region]}getAuthorizationHeader(){return n(this,void 0,void 0,(function*(){return this.accessTokens[this.region]}))}setAccessTokens(e){this.accessTokens=e}loadTokens(){let e=o.resolve(a.homedir(),p),t=this.readCredentialsFile(e);u.isNotEmptyObject(t)&&this.setAccessTokens(Object.assign(Object.assign({},t),t.token&&!t[this.region]&&{[this.region]:t.token})),c.env.REDOCLY_AUTHORIZATION&&this.setAccessTokens(Object.assign(Object.assign({},this.accessTokens),{[this.region]:c.env.REDOCLY_AUTHORIZATION}))}getAllTokens(){return Object.entries(this.accessTokens).filter((([e])=>c.AVAILABLE_REGIONS.includes(e))).map((([e,t])=>({region:e,token:t})))}getValidTokens(){return n(this,void 0,void 0,(function*(){let e=this.getAllTokens(),t=yield Promise.allSettled(e.map((({token:e,region:t})=>this.verifyToken(e,t))));return e.filter(((e,r)=>"fulfilled"===t[r].status)).map((({token:e,region:t})=>({token:e,region:t,valid:!0})))}))}getTokens(){return n(this,void 0,void 0,(function*(){return this.hasTokens()?yield this.getValidTokens():[]}))}isAuthorizedWithRedoclyByRegion(){return n(this,void 0,void 0,(function*(){if(!this.hasTokens())return!1;let e=this.accessTokens[this.region];if(!e)return!1;try{return yield this.verifyToken(e,this.region),!0}catch(e){return!1}}))}isAuthorizedWithRedocly(){return n(this,void 0,void 0,(function*(){return this.hasTokens()&&u.isNotEmptyObject(yield this.getValidTokens())}))}readCredentialsFile(e){return i.existsSync(e)?JSON.parse(i.readFileSync(e,"utf-8")):{}}verifyToken(e,t,r=!1){return n(this,void 0,void 0,(function*(){return this.registryApi.authStatus(e,t,r)}))}login(e,t=!1){return n(this,void 0,void 0,(function*(){let r=o.resolve(a.homedir(),p);try{yield this.verifyToken(e,this.region,t)}catch(e){throw Error("Authorization failed. Please check if you entered a valid API key.")}let n=Object.assign(Object.assign({},this.readCredentialsFile(r)),{[this.region]:e,token:e});this.accessTokens=n,this.registryApi.setAccessTokens(n),i.writeFileSync(r,JSON.stringify(n,null,2))}))}logout(){let e=o.resolve(a.homedir(),p);i.existsSync(e)&&i.unlinkSync(e)}},t.isRedoclyRegistryURL=function(e){let t=c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION];return!(!e.startsWith(`https://api.${t}/registry/`)&&!e.startsWith(`https://api.${"redocly.com"===t?"redoc.ly":t}/registry/`))}},1390:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryApi=void 0;let i=r(8150),o=r(3777),a=r(771),s=r(3244).i8;t.RegistryApi=class{constructor(e,t){this.accessTokens=e,this.region=t}get accessToken(){return a.isNotEmptyObject(this.accessTokens)&&this.accessTokens[this.region]}getBaseUrl(e=o.DEFAULT_REGION){return`https://api.${o.DOMAINS[e]}/registry`}setAccessTokens(e){return this.accessTokens=e,this}request(e="",t={},r){return n(this,void 0,void 0,(function*(){let n=Object.assign({},t.headers||{},{"x-redocly-cli-version":s});if(!n.hasOwnProperty("authorization"))throw Error("Unauthorized");let o=yield i.default(`${this.getBaseUrl(r)}${e}`,Object.assign({},t,{headers:n}));if(401===o.status)throw Error("Unauthorized");if(404===o.status){let e=yield o.json();throw Error(e.code)}return o}))}authStatus(e,t,r=!1){return n(this,void 0,void 0,(function*(){try{let r=yield this.request("",{headers:{authorization:e}},t);return yield r.json()}catch(e){throw r&&console.log(e),e}}))}prepareFileUpload({organizationId:e,name:t,version:r,filesHash:i,filename:o,isUpsert:a}){return n(this,void 0,void 0,(function*(){let n=yield this.request(`/${e}/${t}/${r}/prepare-file-upload`,{method:"POST",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({filesHash:i,filename:o,isUpsert:a})},this.region);if(n.ok)return n.json();throw Error("Could not prepare file upload")}))}pushApi({organizationId:e,name:t,version:r,rootFilePath:i,filePaths:o,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u}){return n(this,void 0,void 0,(function*(){if(!(yield this.request(`/${e}/${t}/${r}`,{method:"PUT",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({rootFilePath:i,filePaths:o,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u})},this.region)).ok)throw Error("Could not push api")}))}}},7468:function(e,t){"use strict";function r(e,t){return""===e&&(e="#/"),"/"===e[e.length-1]?e+t:e+"/"+t}Object.defineProperty(t,"__esModule",{value:!0}),t.isMappingRef=t.isAbsoluteUrl=t.refBaseName=t.pointerBaseName=t.parsePointer=t.parseRef=t.escapePointer=t.unescapePointer=t.Location=t.isRef=t.joinPointer=void 0,t.joinPointer=r,t.isRef=function(e){return e&&"string"==typeof e.$ref};class n{constructor(e,t){this.source=e,this.pointer=t}child(e){return new n(this.source,r(this.pointer,(Array.isArray(e)?e:[e]).map(o).join("/")))}key(){return Object.assign(Object.assign({},this),{reportOnKey:!0})}get absolutePointer(){return this.source.absoluteRef+("#/"===this.pointer?"":this.pointer)}}function i(e){return decodeURIComponent(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function o(e){return"number"==typeof e?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}t.Location=n,t.unescapePointer=i,t.escapePointer=o,t.parseRef=function(e){let[t,r]=e.split("#/");return{uri:t||null,pointer:r?r.split("/").map(i).filter(Boolean):[]}},t.parsePointer=function(e){return e.substr(2).split("/").map(i)},t.pointerBaseName=function(e){let t=e.split("/");return t[t.length-1]},t.refBaseName=function(e){let t=e.split(/[\/\\]/);return t[t.length-1].replace(/\.[^.]+$/,"")},t.isAbsoluteUrl=function(e){return e.startsWith("http://")||e.startsWith("https://")},t.isMappingRef=function(e){return e.startsWith("#")||e.startsWith("https://")||e.startsWith("http://")||e.startsWith("./")||e.startsWith("../")||e.indexOf("/")>-1}},4182:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.resolveDocument=t.BaseResolver=t.makeDocumentFromString=t.makeRefId=t.YamlParseError=t.ResolveError=t.Source=void 0;let i=r(3197),o=r(6470),a=r(7468),s=r(5220),l=r(771);class c{constructor(e,t,r){this.absoluteRef=e,this.body=t,this.mimeType=r}getAst(e){var t;return void 0===this._ast&&(this._ast=null!==(t=e(this.body,{filename:this.absoluteRef}))&&void 0!==t?t:void 0,this._ast&&0===this._ast.kind&&""===this._ast.value&&1!==this._ast.startPosition&&(this._ast.startPosition=1,this._ast.endPosition=1)),this._ast}getLines(){return void 0===this._lines&&(this._lines=this.body.split(/\r\n|[\n\r]/g)),this._lines}}t.Source=c;class u extends Error{constructor(e){super(e.message),this.originalError=e,Object.setPrototypeOf(this,u.prototype)}}t.ResolveError=u;class p extends Error{constructor(e,t){super(e.message.split("\n")[0]),this.originalError=e,this.source=t,Object.setPrototypeOf(this,p.prototype);let[,r,n]=this.message.match(/\((\d+):(\d+)\)$/)||[];this.line=parseInt(r,10),this.col=parseInt(n,10)}}function d(e,t){return e+"::"+t}function f(e,t){return{prev:e,node:t}}t.YamlParseError=p,t.makeRefId=d,t.makeDocumentFromString=function(e,t){let r=new c(t,e);try{return{source:r,parsed:l.parseYaml(e,{filename:t})}}catch(e){throw new p(e,r)}},t.BaseResolver=class{constructor(e={http:{headers:[]}}){this.config=e,this.cache=new Map}getFiles(){return new Set(Array.from(this.cache.keys()))}resolveExternalRef(e,t){return a.isAbsoluteUrl(t)?t:e&&a.isAbsoluteUrl(e)?new URL(t,e).href:o.resolve(e?o.dirname(e):process.cwd(),t)}loadExternalRef(e){return n(this,void 0,void 0,(function*(){try{if(a.isAbsoluteUrl(e)){let{body:t,mimeType:r}=yield l.readFileFromUrl(e,this.config.http);return new c(e,t,r)}return new c(e,yield i.promises.readFile(e,"utf-8"))}catch(e){throw new u(e)}}))}parseDocument(e,t=!1){var r;let n=e.absoluteRef.substr(e.absoluteRef.lastIndexOf("."));if(![".json",".json",".yml",".yaml"].includes(n)&&!(null===(r=e.mimeType)||void 0===r?void 0:r.match(/(json|yaml|openapi)/))&&!t)return{source:e,parsed:e.body};try{return{source:e,parsed:l.parseYaml(e.body,{filename:e.absoluteRef})}}catch(t){throw new p(t,e)}}resolveDocument(e,t,r=!1){return n(this,void 0,void 0,(function*(){let n=this.resolveExternalRef(e,t),i=this.cache.get(n);if(i)return i;let o=this.loadExternalRef(n).then((e=>this.parseDocument(e,r)));return this.cache.set(n,o),o}))}};let h={name:"unknown",properties:{}},m={name:"scalar",properties:{}};t.resolveDocument=function(e){return n(this,void 0,void 0,(function*(){let t,{rootDocument:r,externalRefResolver:i,rootType:o}=e,l=new Map,c=new Set,u=[];!function e(t,r,o,p){!function t(o,p,g){if("object"!=typeof o||null===o)return;let y=`${p.name}::${g}`;if(!c.has(y))if(c.add(y),Array.isArray(o)){let e=p.items;if(p!==h&&void 0===e)return;for(let r=0;r{t.resolved&&e(t.node,t.document,t.nodePointer,p)}));u.push(t)}}}(t,p,r.source.absoluteRef+"#/")}(r.parsed,r,0,o);do{t=yield Promise.all(u)}while(u.length!==t.length);return l}))}},7275:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateJsonSchema=t.releaseAjvInstance=void 0;let n=r(5499),i=r(7468),o=null;t.releaseAjvInstance=function(){o=null},t.validateJsonSchema=function(e,t,r,a,s,l){let c=function(e,t,r,i){var a,s;let l=(a=r,s=i,o||(o=new n.default({schemaId:"$id",meta:!0,allErrors:!0,strictSchema:!1,inlineRefs:!1,validateSchema:!1,discriminator:!0,allowUnionTypes:!0,validateFormats:!1,defaultAdditionalProperties:!s,loadSchemaSync(e,t){let r=a({$ref:t},e.split("#")[0]);return!(!r||!r.location)&&Object.assign({$id:r.location.absolutePointer},r.node)},logger:!1})),o);return l.getSchema(t.absolutePointer)||l.addSchema(Object.assign({$id:t.absolutePointer},e),t.absolutePointer),l.getSchema(t.absolutePointer)}(t,r,s,l);return c?{valid:!!c(e,{instancePath:a,parentData:{fake:{}},parentDataProperty:"fake",rootData:{},dynamicAnchors:{}}),errors:(c.errors||[]).map((function(e){let t=e.message,r="enum"===e.keyword?e.params.allowedValues:void 0;r&&(t+=` ${r.map((e=>`"${e}"`)).join(", ")}`),"type"===e.keyword&&(t=`type ${t}`);let n=e.instancePath.substring(a.length+1),o=n.substring(n.lastIndexOf("/")+1);if(o&&(t=`\`${o}\` property ${t}`),"additionalProperties"===e.keyword){let r=e.params.additionalProperty;t=`${t} \`${r}\``,e.instancePath+="/"+i.escapePointer(r)}return Object.assign(Object.assign({},e),{message:t,suggest:r})}))}:{valid:!0,errors:[]}}},9740:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.asserts=t.runOnValuesSet=t.runOnKeysSet=void 0;let n=r(771),i=r(5738);t.runOnKeysSet=new Set(["mutuallyExclusive","mutuallyRequired","enum","pattern","minLength","maxLength","casing","sortOrder","disallowed","required","requireAny","ref"]),t.runOnValuesSet=new Set(["pattern","enum","defined","undefined","nonEmpty","minLength","maxLength","casing","sortOrder","ref"]),t.asserts={pattern(e,t,r){if(void 0===e)return{isValid:!0};let o=n.isString(e)?[e]:e,a=i.regexFromString(t);for(let t of o)if(!(null==a?void 0:a.test(t)))return{isValid:!1,location:n.isString(e)?r:r.key()};return{isValid:!0}},enum(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i)if(!t.includes(o))return{isValid:!1,location:n.isString(e)?r:r.child(o).key()};return{isValid:!0}},defined(e,t=!0,r){let n=void 0!==e;return{isValid:t?n:!n,location:r}},required(e,t,r){for(let n of t)if(!e.includes(n))return{isValid:!1,location:r.key()};return{isValid:!0}},disallowed(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i)if(t.includes(o))return{isValid:!1,location:n.isString(e)?r:r.child(o).key()};return{isValid:!0}},undefined(e,t=!0,r){let n=void 0===e;return{isValid:t?n:!n,location:r}},nonEmpty(e,t=!0,r){let n=null==e||""===e;return{isValid:t?!n:n,location:r}},minLength:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:e.length>=t,location:r},maxLength:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:e.length<=t,location:r},casing(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i){let i=!1;switch(t){case"camelCase":i=!!o.match(/^[a-z][a-zA-Z0-9]+$/g);break;case"kebab-case":i=!!o.match(/^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/g);break;case"snake_case":i=!!o.match(/^([a-z][a-z0-9]*)(_[a-z0-9]+)*$/g);break;case"PascalCase":i=!!o.match(/^[A-Z][a-zA-Z0-9]+$/g);break;case"MACRO_CASE":i=!!o.match(/^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g);break;case"COBOL-CASE":i=!!o.match(/^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g);break;case"flatcase":i=!!o.match(/^[a-z][a-z0-9]+$/g)}if(!i)return{isValid:!1,location:n.isString(e)?r:r.child(o).key()}}return{isValid:!0}},sortOrder:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:i.isOrdered(e,t),location:r},mutuallyExclusive:(e,t,r)=>({isValid:2>i.getIntersectionLength(e,t),location:r.key()}),mutuallyRequired:(e,t,r)=>({isValid:!(i.getIntersectionLength(e,t)>0)||i.getIntersectionLength(e,t)===t.length,location:r.key()}),requireAny:(e,t,r)=>({isValid:i.getIntersectionLength(e,t)>=1,location:r.key()}),ref(e,t,r,n){if(void 0===n)return{isValid:!0};let o=n.hasOwnProperty("$ref");if("boolean"==typeof t)return{isValid:t?o:!o,location:o?r:r.key()};let a=i.regexFromString(t);return{isValid:o&&(null==a?void 0:a.test(n.$ref)),location:o?r:r.key()}}}},4015:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Assertions=void 0;let n=r(9740),i=r(5738);t.Assertions=e=>{let t=[],r=Object.values(e).filter((e=>"object"==typeof e&&null!==e));for(let[e,o]of r.entries()){let r=o.assertionId&&`${o.assertionId} assertion`||`assertion #${e+1}`;if(!o.subject)throw Error(`${r}: 'subject' is required`);let a=Array.isArray(o.subject)?o.subject:[o.subject],s=Object.keys(n.asserts).filter((e=>void 0!==o[e])).map((e=>({assertId:r,name:e,conditions:o[e],message:o.message,severity:o.severity||"error",suggest:o.suggest||[],runsOnKeys:n.runOnKeysSet.has(e),runsOnValues:n.runOnValuesSet.has(e)}))),l=s.find((e=>e.runsOnKeys&&!e.runsOnValues)),c=s.find((e=>e.runsOnValues&&!e.runsOnKeys));if(c&&!o.property)throw Error(`${c.name} can't be used on all keys. Please provide a single property.`);if(l&&o.property)throw Error(`${l.name} can't be used on a single property. Please use 'property'.`);for(let e of a){let r=i.buildSubjectVisitor(o.property,s,o.context),n=i.buildVisitorObject(e,o.context,r);t.push(n)}}return t}},5738:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexFromString=t.isOrdered=t.getIntersectionLength=t.buildSubjectVisitor=t.buildVisitorObject=void 0;let n=r(7468),i=r(9740);function o({values:e,rawValues:t,assert:r,location:n,report:o}){let a=i.asserts[r.name](e,r.conditions,n,t);a.isValid||o({message:r.message||`The ${r.assertId} doesn't meet required conditions`,location:a.location||n,forceSeverity:r.severity,suggest:r.suggest,ruleId:r.assertId})}t.buildVisitorObject=function(e,t,r){if(!t)return{[e]:r};let n={},i=n;for(let r=0;ro?!o.includes(t):a?a.includes(t):void 0}:{},n=n[i.type]}return n[e]=r,i},t.buildSubjectVisitor=function(e,t,r){return(i,{report:a,location:s,rawLocation:l,key:c,type:u,resolve:p,rawNode:d})=>{var f;if(r){let e=r[r.length-1];if(e.type===u.name){let t=e.matchParentKeys,r=e.excludeParentKeys;if(t&&!t.includes(c)||r&&r.includes(c))return}}for(let r of(e&&(e=Array.isArray(e)?e:[e]),t)){let t="ref"===r.name?l:s;if(e)for(let s of e)o({values:n.isRef(i[s])?null===(f=p(i[s]))||void 0===f?void 0:f.node:i[s],rawValues:d[s],assert:r,location:t.child(s),report:a});else{let e="ref"===r.name?d:Object.keys(i);o({values:Object.keys(i),rawValues:e,assert:r,location:t,report:a})}}}},t.getIntersectionLength=function(e,t){let r=new Set(t),n=0;for(let t of e)r.has(t)&&n++;return n},t.isOrdered=function(e,t){let r=t.direction||t,n=t.property;for(let t=1;t=o:i<=o))return!1}return!0},t.regexFromString=function(e){let t=e.match(/^\/(.*)\/(.*)|(.*)/);return t&&RegExp(t[1]||t[3],t[2])}},8265:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoContact=void 0;let n=r(780);t.InfoContact=()=>({Info(e,{report:t,location:r}){e.contact||t({message:n.missingRequiredField("Info","contact"),location:r.child("contact").key()})}})},8675:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescription=void 0;let n=r(780);t.InfoDescription=()=>({Info(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},9622:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicense=void 0;let n=r(780);t.InfoLicense=()=>({Info(e,{report:t}){e.license||t({message:n.missingRequiredField("Info","license"),location:{reportOnKey:!0}})}})},476:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicenseUrl=void 0;let n=r(780);t.InfoLicenseUrl=()=>({License(e,t){n.validateDefinedAndNonEmpty("url",e,t)}})},3467:function(e,t){"use strict";function r(e,t){let r=e.split("/"),n=t.split("/");if(r.length!==n.length)return!1;let i=0,o=0,a=!0;for(let e=0;e({PathMap(e,{report:t,location:n}){let i=[];for(let o of Object.keys(e)){let e=i.find((e=>r(e,o)));e&&t({message:`Paths should resolve unambiguously. Found two ambiguous paths: \`${e}\` and \`${o}\`.`,location:n.child([o]).key()}),i.push(o)}}})},2319:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEnumTypeMismatch=void 0;let n=r(780);t.NoEnumTypeMismatch=()=>({Schema(e,{report:t,location:r}){if(!e.enum||Array.isArray(e.enum)){if(e.enum&&e.type&&!Array.isArray(e.type)){let i=e.enum.filter((t=>!n.matchesJsonSchemaType(t,e.type,e.nullable)));for(let o of i)t({message:`All values of \`enum\` field must be of the same type as the \`type\` field: expected "${e.type}" but received "${n.oasTypeOf(o)}".`,location:r.child(["enum",e.enum.indexOf(o)])})}if(e.enum&&e.type&&Array.isArray(e.type)){let i={};for(let t of e.enum){for(let r of(i[t]=[],e.type))n.matchesJsonSchemaType(t,r,e.nullable)||i[t].push(r);i[t].length!==e.type.length&&delete i[t]}for(let n of Object.keys(i))t({message:`Enum value \`${n}\` must be of one type. Allowed types: \`${e.type}\`.`,location:r.child(["enum",e.enum.indexOf(n)])})}}}})},525:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoHttpVerbsInPaths=void 0;let n=r(771),i=["get","head","post","put","patch","delete","options","trace"];t.NoHttpVerbsInPaths=({splitIntoWords:e})=>({PathItem(t,{key:r,report:o,location:a}){let s=r.toString();if(!s.startsWith("/"))return;let l=s.split("/");for(let t of l){if(!t||n.isPathParameter(t))continue;let r=r=>e?n.splitCamelCaseIntoWords(t).has(r):t.toLocaleLowerCase().includes(r);for(let e of i)r(e)&&o({message:`path \`${s}\` should not contain http verb ${e}`,location:a.key()})}}})},4628:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoIdenticalPaths=void 0,t.NoIdenticalPaths=()=>({PathMap(e,{report:t,location:r}){let n=new Map;for(let i of Object.keys(e)){let e=i.replace(/{.+?}/g,"{VARIABLE}"),o=n.get(e);o?t({message:`The path already exists which differs only by path parameter name(s): \`${o}\` and \`${i}\`.`,location:r.child([i]).key()}):n.set(e,i)}}})},1562:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidParameterExamples=void 0;let n=r(780);t.NoInvalidParameterExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Parameter:{leave(e,t){if(e.example&&n.validateExample(e.example,e.schema,t.location.child("example"),t,r),e.examples)for(let[r,i]of Object.entries(e.examples))"value"in i&&n.validateExample(i.value,e.schema,t.location.child(["examples",r]),t,!1)}}}}},78:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidSchemaExamples=void 0;let n=r(780);t.NoInvalidSchemaExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Schema:{leave(e,t){if(e.examples)for(let i of e.examples)n.validateExample(i,e,t.location.child(["examples",e.examples.indexOf(i)]),t,r);e.example&&n.validateExample(e.example,e,t.location.child("example"),t,!1)}}}}},700:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoPathTrailingSlash=void 0,t.NoPathTrailingSlash=()=>({PathItem(e,{report:t,key:r,location:n}){r.endsWith("/")&&"/"!==r&&t({message:`\`${r}\` should not have a trailing slash.`,location:n.key()})}})},5946:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation2xxResponse=void 0,t.Operation2xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>"default"===e||/2[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `2xx` response.",location:{reportOnKey:!0}})}})},5281:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation4xxResponse=void 0,t.Operation4xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>/4[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `4xx` response.",location:{reportOnKey:!0}})}})},3408:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescription=void 0;let n=r(780);t.OperationDescription=()=>({Operation(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},8742:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUnique=void 0,t.OperationIdUnique=()=>{let e=new Set;return{Operation(t,{report:r,location:n}){t.operationId&&(e.has(t.operationId)&&r({message:"Every operation must have a unique `operationId`.",location:n.child([t.operationId])}),e.add(t.operationId))}}}},5064:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUrlSafe=void 0;let r=/^[A-Za-z0-9-._~:/?#\[\]@!\$&'()*+,;=]*$/;t.OperationIdUrlSafe=()=>({Operation(e,{report:t,location:n}){e.operationId&&!r.test(e.operationId)&&t({message:"Operation `operationId` should not have URL invalid characters.",location:n.child(["operationId"])})}})},8786:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationOperationId=void 0;let n=r(780);t.OperationOperationId=()=>({DefinitionRoot:{PathItem:{Operation(e,t){n.validateDefinedAndNonEmpty("operationId",e,t)}}}})},4112:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationParametersUnique=void 0,t.OperationParametersUnique=()=>{let e,t;return{PathItem:{enter(){e=new Set},Parameter(t,{report:r,key:n,parentLocations:i}){let o=`${t.in}___${t.name}`;e.has(o)&&r({message:`Paths must have unique \`name\` + \`in\` parameters.\nRepeats of \`in:${t.in}\` + \`name:${t.name}\`.`,location:i.PathItem.child(["parameters",n])}),e.add(`${t.in}___${t.name}`)},Operation:{enter(){t=new Set},Parameter(e,{report:r,key:n,parentLocations:i}){let o=`${e.in}___${e.name}`;t.has(o)&&r({message:`Operations must have unique \`name\` + \`in\` parameters. Repeats of \`in:${e.in}\` + \`name:${e.name}\`.`,location:i.Operation.child(["parameters",n])}),t.add(o)}}}}}},7892:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSecurityDefined=void 0,t.OperationSecurityDefined=()=>{let e=new Map;return{DefinitionRoot:{leave(t,{report:r}){for(let[t,n]of e.entries())if(!n.defined)for(let e of n.from)r({message:`There is no \`${t}\` security scheme defined.`,location:e.key()})}},SecurityScheme(t,{key:r}){e.set(r.toString(),{defined:!0,from:[]})},SecurityRequirement(t,{location:r}){for(let n of Object.keys(t)){let t=e.get(n),i=r.child([n]);t?t.from.push(i):e.set(n,{from:[i]})}}}}},8613:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSingularTag=void 0,t.OperationSingularTag=()=>({Operation(e,{report:t,location:r}){e.tags&&e.tags.length>1&&t({message:"Operation `tags` object should have only one tag.",location:r.child(["tags"]).key()})}})},9578:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSummary=void 0;let n=r(780);t.OperationSummary=()=>({Operation(e,t){n.validateDefinedAndNonEmpty("summary",e,t)}})},5097:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationTagDefined=void 0,t.OperationTagDefined=()=>{let e;return{DefinitionRoot(t){var r;e=new Set((null!==(r=t.tags)&&void 0!==r?r:[]).map((e=>e.name)))},Operation(t,{report:r,location:n}){if(t.tags)for(let i=0;i({Parameter(e,{report:t,location:r}){void 0===e.description?t({message:"Parameter object description must be present.",location:{reportOnKey:!0}}):e.description||t({message:"Parameter object description must be non-empty string.",location:r.child(["description"])})}})},7890:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathDeclarationMustExist=void 0,t.PathDeclarationMustExist=()=>({PathItem(e,{report:t,key:r}){-1!==r.indexOf("{}")&&t({message:"Path parameter declarations must be non-empty. `{}` is invalid.",location:{reportOnKey:!0}})}})},3689:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathExcludesPatterns=void 0,t.PathExcludesPatterns=({patterns:e})=>({PathItem(t,{report:r,key:n,location:i}){if(!e)throw Error('Parameter "patterns" is not provided for "path-excludes-patterns" rule');let o=n.toString();if(o.startsWith("/")){let t=e.filter((e=>o.match(e)));for(let e of t)r({message:`path \`${o}\` should not match regex pattern: \`${e}\``,location:i.key()})}}})},2332:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathHttpVerbsOrder=void 0;let r=["get","head","post","put","patch","delete","options","trace"];t.PathHttpVerbsOrder=e=>{let t=e&&e.order||r;if(!Array.isArray(t))throw Error("path-http-verbs-order `order` option must be an array");return{PathItem(e,{report:r,location:n}){let i=Object.keys(e).filter((e=>t.includes(e)));for(let e=0;e({PathMap:{PathItem(e,{report:t,key:r}){r.toString().includes("?")&&t({message:"Don't put query string items in the path, they belong in parameters with `in: query`.",location:{reportOnKey:!0}})}}})},7421:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathParamsDefined=void 0;let r=/\{([a-zA-Z0-9_.-]+)\}+/g;t.PathParamsDefined=()=>{let e,t,n;return{PathItem:{enter(i,{key:o}){t=new Set,n=o,e=new Set(Array.from(o.toString().matchAll(r)).map((e=>e[1])))},Parameter(r,{report:i,location:o}){"path"===r.in&&r.name&&(t.add(r.name),e.has(r.name)||i({message:`Path parameter \`${r.name}\` is not used in the path \`${n}\`.`,location:o.child(["name"])}))},Operation:{leave(r,{report:i,location:o}){for(let r of Array.from(e.keys()))t.has(r)||i({message:`The operation does not define the path parameter \`{${r}}\` expected by path \`${n}\`.`,location:o.child(["parameters"]).key()})},Parameter(r,{report:i,location:o}){"path"===r.in&&r.name&&(t.add(r.name),e.has(r.name)||i({message:`Path parameter \`${r.name}\` is not used in the path \`${n}\`.`,location:o.child(["name"])}))}}}}}},3807:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathSegmentPlural=void 0;let n=r(771);t.PathSegmentPlural=e=>{let{ignoreLastPathSegment:t,exceptions:r}=e;return{PathItem:{leave(e,{report:i,key:o,location:a}){let s=o.toString();if(s.startsWith("/")){let e=s.split("/");for(let o of(e.shift(),t&&e.length>1&&e.pop(),e))r&&r.includes(o)||!n.isPathParameter(o)&&n.isSingular(o)&&i({message:`path segment \`${o}\` should be plural.`,location:a.key()})}}}}}},9527:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathsKebabCase=void 0,t.PathsKebabCase=()=>({PathItem(e,{report:t,key:r}){r.substr(1).split("/").filter((e=>""!==e)).every((e=>/^{.+}$/.test(e)||/^[a-z0-9-.]+$/.test(e)))||t({message:`\`${r}\` does not use kebab-case.`,location:{reportOnKey:!0}})}})},5839:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsHeader=void 0;let n=r(771);t.ResponseContainsHeader=e=>{let t=e.names||{};return{Operation:{Response:{enter(e,{report:r,location:i,key:o}){var a;let s=t[o]||t[n.getMatchingStatusCodeRange(o)]||t[n.getMatchingStatusCodeRange(o).toLowerCase()]||[];for(let t of s)(null===(a=e.headers)||void 0===a?void 0:a[t])||r({message:`Response object must contain a "${t}" header.`,location:i.child("headers").key()})}}}}}},5669:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ScalarPropertyMissingExample=void 0;let n=r(1510),i=["string","integer","number","boolean","null"];t.ScalarPropertyMissingExample=()=>({SchemaProperties(e,{report:t,location:r,oasVersion:o,resolve:a}){var s;for(let l of Object.keys(e)){let c=a(e[l]).node;c&&(s=c).type&&!(s.allOf||s.anyOf||s.oneOf)&&"binary"!==s.format&&(Array.isArray(s.type)?s.type.every((e=>i.includes(e))):i.includes(s.type))&&void 0===c.example&&void 0===c.examples&&t({message:`Scalar property should have "example"${o===n.OasVersion.Version3_1?' or "examples"':""} defined.`,location:r.child(l).key()})}}})},6471:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OasSpec=void 0;let n=r(5220),i=r(780),o=r(7468),a=r(771);t.OasSpec=()=>({any(e,{report:t,type:r,location:s,key:l,resolve:c,ignoreNextVisitorsOnNode:u}){var p,d,f,h;let m=i.oasTypeOf(e);if(r.items)return void("array"!==m&&(t({message:`Expected type \`${r.name}\` (array) but got \`${m}\``}),u()));if("object"!==m)return t({message:`Expected type \`${r.name}\` (object) but got \`${m}\``}),void u();let g="function"==typeof r.required?r.required(e,l):r.required;for(let r of g||[])e.hasOwnProperty(r)||t({message:`The field \`${r}\` must be present on this level.`,location:[{reportOnKey:!0}]});let y=null===(p=r.allowed)||void 0===p?void 0:p.call(r,e);if(y&&a.isPlainObject(e))for(let n in e)y.includes(n)||r.extensionsPrefix&&n.startsWith(r.extensionsPrefix)||!Object.keys(r.properties).includes(n)||t({message:`The field \`${n}\` is not allowed here.`,location:s.child([n]).key()});let v=r.requiredOneOf||null;if(v){let n=!1;for(let t of v||[])e.hasOwnProperty(t)&&(n=!0);n||t({message:`Must contain at least one of the following fields: ${null===(d=r.requiredOneOf)||void 0===d?void 0:d.join(", ")}.`,location:[{reportOnKey:!0}]})}for(let a of Object.keys(e)){let l=s.child([a]),u=e[a],p=r.properties[a];if(void 0===p&&(p=r.additionalProperties),"function"==typeof p&&(p=p(u,a)),n.isNamedType(p))continue;let d=p,m=i.oasTypeOf(u);if(void 0!==d){if(null!==d){if(!1!==d.resolvable&&o.isRef(u)&&(u=c(u).node),d.enum)d.enum.includes(u)||t({location:l,message:`\`${a}\` can be one of the following only: ${d.enum.map((e=>`"${e}"`)).join(", ")}.`,suggest:i.getSuggest(u,d.enum)});else if(d.type&&!i.matchesJsonSchemaType(u,d.type,!1))t({message:`Expected type \`${d.type}\` but got \`${m}\`.`,location:l});else if("array"===m&&(null===(f=d.items)||void 0===f?void 0:f.type)){let e=null===(h=d.items)||void 0===h?void 0:h.type;for(let r=0;re[a]&&t({message:`The value of the ${a} field must be greater than or equal to ${d.minimum}`,location:s.child([a])})}}else{if(a.startsWith("x-"))continue;t({message:`Property \`${a}\` is not expected here.`,suggest:i.getSuggest(a,Object.keys(r.properties)),location:l.key()})}}}})},7281:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagDescription=void 0;let n=r(780);t.TagDescription=()=>({Tag(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},6855:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagsAlphabetical=void 0,t.TagsAlphabetical=()=>({DefinitionRoot(e,{report:t,location:r}){if(e.tags)for(let n=0;ne.tags[n+1].name&&t({message:"The `tags` array should be in alphabetical order.",location:r.child(["tags",n])})}})},348:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reportUnresolvedRef=t.NoUnresolvedRefs=void 0;let n=r(4182);function i(e,t,r){var i;let o=e.error;o instanceof n.YamlParseError&&t({message:"Failed to parse: "+o.message,location:{source:o.source,pointer:void 0,start:{col:o.col,line:o.line}}});let a=null===(i=e.error)||void 0===i?void 0:i.message;t({location:r,message:"Can't resolve $ref"+(a?": "+a:"")})}t.NoUnresolvedRefs=()=>({ref:{leave(e,{report:t,location:r},n){void 0===n.node&&i(n,t,r)}},DiscriminatorMapping(e,{report:t,resolve:r,location:n}){for(let o of Object.keys(e)){let a=r({$ref:e[o]});if(void 0!==a.node)return;i(a,t,n.child(o))}}}),t.reportUnresolvedRef=i},9566:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{let t=e.prefixes||["is","has"],r=RegExp(`^(${t.join("|")})[A-Z-_]`),n=t.map((e=>`\`${e}\``)),i=1===n.length?n[0]:n.slice(0,-1).join(", ")+" or "+n[t.length-1];return{Parameter(e,{report:t,location:n}){"boolean"!==e.type||r.test(e.name)||t({message:`Boolean parameter \`${e.name}\` should have ${i} prefix.`,location:n.child("name")})}}}},7523:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;let n=r(6471),i=r(78),o=r(1562),a=r(8675),s=r(8265),l=r(9622),c=r(476),u=r(9566),p=r(7281),d=r(6855),f=r(9527),h=r(2319),m=r(700),g=r(5946),y=r(5281),v=r(4015),b=r(8742),x=r(4112),w=r(7421),k=r(5097),_=r(7890),O=r(5064),S=r(3408),E=r(5023),P=r(3529),A=r(8613),$=r(7892),C=r(348),R=r(2332),j=r(4628),T=r(8786),I=r(9578),N=r(3467),L=r(525),D=r(3689),M=r(7028),F=r(1750),z=r(3807),U=r(5839),V=r(7899),B=r(5669);t.rules={spec:n.OasSpec,"no-invalid-schema-examples":i.NoInvalidSchemaExamples,"no-invalid-parameter-examples":o.NoInvalidParameterExamples,"info-description":a.InfoDescription,"info-contact":s.InfoContact,"info-license":l.InfoLicense,"info-license-url":c.InfoLicenseUrl,"tag-description":p.TagDescription,"tags-alphabetical":d.TagsAlphabetical,"paths-kebab-case":f.PathsKebabCase,"no-enum-type-mismatch":h.NoEnumTypeMismatch,"boolean-parameter-prefixes":u.BooleanParameterPrefixes,"no-path-trailing-slash":m.NoPathTrailingSlash,"operation-2xx-response":g.Operation2xxResponse,"operation-4xx-response":y.Operation4xxResponse,assertions:v.Assertions,"operation-operationId-unique":b.OperationIdUnique,"operation-parameters-unique":x.OperationParametersUnique,"path-parameters-defined":w.PathParamsDefined,"operation-tag-defined":k.OperationTagDefined,"path-declaration-must-exist":_.PathDeclarationMustExist,"operation-operationId-url-safe":O.OperationIdUrlSafe,"operation-operationId":T.OperationOperationId,"operation-summary":I.OperationSummary,"operation-description":S.OperationDescription,"path-not-include-query":E.PathNotIncludeQuery,"path-params-defined":w.PathParamsDefined,"parameter-description":P.ParameterDescription,"operation-singular-tag":A.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"no-identical-paths":j.NoIdenticalPaths,"no-ambiguous-paths":N.NoAmbiguousPaths,"path-http-verbs-order":R.PathHttpVerbsOrder,"no-http-verbs-in-paths":L.NoHttpVerbsInPaths,"path-excludes-patterns":D.PathExcludesPatterns,"request-mime-type":M.RequestMimeType,"response-mime-type":F.ResponseMimeType,"path-segment-plural":z.PathSegmentPlural,"response-contains-header":U.ResponseContainsHeader,"response-contains-property":V.ResponseContainsProperty,"scalar-property-missing-example":B.ScalarPropertyMissingExample},t.preprocessors={}},4508:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;let n=r(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,r,n){var i;e.set(t.absolutePointer,{used:(null===(i=e.get(t.absolutePointer))||void 0===i?void 0:i.used)||!1,componentType:r,name:n})}return{ref:{leave(t,{type:r,resolve:n,key:i}){if(["Schema","Parameter","Response","SecurityScheme"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString()})}}},DefinitionRoot:{leave(t,r){let i=r.getVisitorData();i.removedCount=0;let o=new Set;for(let r of(e.forEach((e=>{let{used:r,name:n,componentType:a}=e;!r&&a&&(o.add(a),delete t[a][n],i.removedCount++)})),o))n.isEmptyObject(t[r])&&delete t[r]}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,"definitions",n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,"parameters",n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,"responses",n.toString())}},NamedSecuritySchemes:{SecurityScheme(e,{location:r,key:n}){t(r,"securityDefinitions",n.toString())}}}}},7028:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;let n=r(771);t.RequestMimeType=({allowedValues:e})=>({DefinitionRoot(t,r){n.validateMimeType({type:"consumes",value:t},r,e)},Operation:{leave(t,r){n.validateMimeType({type:"consumes",value:t},r,e)}}})},7899:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;let n=r(771);t.ResponseContainsProperty=e=>{let t,r=e.names||{};return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter(e,r){t=r.key},Schema(e,{report:i,location:o}){var a;if("object"!==e.type)return;let s=r[t]||r[n.getMatchingStatusCodeRange(t)]||r[n.getMatchingStatusCodeRange(t).toLowerCase()]||[];for(let t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||i({message:`Response object must contain a top-level "${t}" property.`,location:o.child("properties").key()})}}}}}},1750:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;let n=r(771);t.ResponseMimeType=({allowedValues:e})=>({DefinitionRoot(t,r){n.validateMimeType({type:"produces",value:t},r,e)},Operation:{leave(t,r){n.validateMimeType({type:"produces",value:t},r,e)}}})},962:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{let t=e.prefixes||["is","has"],r=RegExp(`^(${t.join("|")})[A-Z-_]`),n=t.map((e=>`\`${e}\``)),i=1===n.length?n[0]:n.slice(0,-1).join(", ")+" or "+n[t.length-1];return{Parameter:{Schema(e,{report:t,parentLocations:n},o){"boolean"!==e.type||r.test(o.Parameter.name)||t({message:`Boolean parameter \`${o.Parameter.name}\` should have ${i} prefix.`,location:n.Parameter.child(["name"])})}}}}},226:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;let n=r(6471),i=r(5946),o=r(5281),a=r(4015),s=r(8742),l=r(4112),c=r(7421),u=r(5097),p=r(1265),d=r(2319),f=r(700),h=r(7890),m=r(5064),g=r(6855),y=r(5486),v=r(2947),b=r(8675),x=r(7281),w=r(8265),k=r(9622),_=r(3408),O=r(897),S=r(5023),E=r(3529),P=r(8613),A=r(476),$=r(7892),C=r(348),R=r(962),j=r(9527),T=r(2332),I=r(7020),N=r(9336),L=r(4628),D=r(6208),M=r(8786),F=r(9578),z=r(3467),U=r(472),V=r(525),B=r(3736),q=r(503),W=r(3807),H=r(3689),Y=r(78),K=r(1562),G=r(5839),Q=r(7557),X=r(5669);t.rules={spec:n.OasSpec,"info-description":b.InfoDescription,"info-contact":w.InfoContact,"info-license":k.InfoLicense,"info-license-url":A.InfoLicenseUrl,"operation-2xx-response":i.Operation2xxResponse,"operation-4xx-response":o.Operation4xxResponse,assertions:a.Assertions,"operation-operationId-unique":s.OperationIdUnique,"operation-parameters-unique":l.OperationParametersUnique,"path-parameters-defined":c.PathParamsDefined,"operation-tag-defined":u.OperationTagDefined,"no-example-value-and-externalValue":p.NoExampleValueAndExternalValue,"no-enum-type-mismatch":d.NoEnumTypeMismatch,"no-path-trailing-slash":f.NoPathTrailingSlash,"no-empty-servers":I.NoEmptyServers,"path-declaration-must-exist":h.PathDeclarationMustExist,"operation-operationId-url-safe":m.OperationIdUrlSafe,"operation-operationId":M.OperationOperationId,"operation-summary":F.OperationSummary,"tags-alphabetical":g.TagsAlphabetical,"no-server-example.com":y.NoServerExample,"no-server-trailing-slash":v.NoServerTrailingSlash,"tag-description":x.TagDescription,"operation-description":_.OperationDescription,"no-unused-components":O.NoUnusedComponents,"path-not-include-query":S.PathNotIncludeQuery,"path-params-defined":c.PathParamsDefined,"parameter-description":E.ParameterDescription,"operation-singular-tag":P.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"paths-kebab-case":j.PathsKebabCase,"boolean-parameter-prefixes":R.BooleanParameterPrefixes,"path-http-verbs-order":T.PathHttpVerbsOrder,"no-invalid-media-type-examples":N.ValidContentExamples,"no-identical-paths":L.NoIdenticalPaths,"no-ambiguous-paths":z.NoAmbiguousPaths,"no-undefined-server-variable":D.NoUndefinedServerVariable,"no-servers-empty-enum":U.NoEmptyEnumServers,"no-http-verbs-in-paths":V.NoHttpVerbsInPaths,"path-excludes-patterns":H.PathExcludesPatterns,"request-mime-type":B.RequestMimeType,"response-mime-type":q.ResponseMimeType,"path-segment-plural":W.PathSegmentPlural,"no-invalid-schema-examples":Y.NoInvalidSchemaExamples,"no-invalid-parameter-examples":K.NoInvalidParameterExamples,"response-contains-header":G.ResponseContainsHeader,"response-contains-property":Q.ResponseContainsProperty,"scalar-property-missing-example":X.ScalarPropertyMissingExample},t.preprocessors={}},7020:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyServers=void 0,t.NoEmptyServers=()=>({DefinitionRoot(e,{report:t,location:r}){e.hasOwnProperty("servers")?Array.isArray(e.servers)&&0!==e.servers.length||t({message:"Servers must be a non-empty array.",location:r.child(["servers"]).key()}):t({message:"Servers must be present.",location:r.child(["openapi"]).key()})}})},1265:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoExampleValueAndExternalValue=void 0,t.NoExampleValueAndExternalValue=()=>({Example(e,{report:t,location:r}){e.value&&e.externalValue&&t({message:"Example object can have either `value` or `externalValue` fields.",location:r.child(["value"]).key()})}})},9336:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ValidContentExamples=void 0;let n=r(7468),i=r(780);t.ValidContentExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{MediaType:{leave(e,t){let{location:o,resolve:a}=t;if(e.schema)if(e.example)s(e.example,o.child("example"));else if(e.examples)for(let t of Object.keys(e.examples))s(e.examples[t],o.child(["examples",t,"value"]),!0);function s(o,s,l){if(n.isRef(o)){let e=a(o);if(!e.location)return;s=l?e.location.child("value"):e.location,o=e.node}i.validateExample(l?o.value:o,e.schema,s,t,r)}}}}}},5486:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerExample=void 0,t.NoServerExample=()=>({Server(e,{report:t,location:r}){-1!==["example.com","localhost"].indexOf(e.url)&&t({message:"Server `url` should not point at example.com.",location:r.child(["url"])})}})},2947:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerTrailingSlash=void 0,t.NoServerTrailingSlash=()=>({Server(e,{report:t,location:r}){e.url&&e.url.endsWith("/")&&"/"!==e.url&&t({message:"Server `url` should not have a trailing slash.",location:r.child(["url"])})}})},472:function(e,t){"use strict";var r,n;function i(e){var t;if(e.variables&&0===Object.keys(e.variables).length)return;let n=[];for(var i in e.variables){let o=e.variables[i];if(!o.enum||(Array.isArray(o.enum)&&0===(null===(t=o.enum)||void 0===t?void 0:t.length)&&n.push(r.empty),!o.default))continue;let a=e.variables[i].default;o.enum&&!o.enum.includes(a)&&n.push(r.invalidDefaultValue)}return n.length?n:void 0}Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyEnumServers=void 0,(n=r||(r={})).empty="empty",n.invalidDefaultValue="invalidDefaultValue",t.NoEmptyEnumServers=()=>({DefinitionRoot(e,{report:t,location:n}){if(!e.servers||0===e.servers.length)return;let o=[];if(Array.isArray(e.servers))for(let t of e.servers){let e=i(t);e&&o.push(...e)}else{let t=i(e.servers);if(!t)return;o.push(...t)}for(let e of o)e===r.empty&&t({message:"Server variable with `enum` must be a non-empty array.",location:n.child(["servers"]).key()}),e===r.invalidDefaultValue&&t({message:"Server variable define `enum` and `default`. `enum` must include default value",location:n.child(["servers"]).key()})}})},6208:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUndefinedServerVariable=void 0,t.NoUndefinedServerVariable=()=>({Server(e,{report:t,location:r}){var n;if(!e.url)return;let i=(null===(n=e.url.match(/{[^}]+}/g))||void 0===n?void 0:n.map((e=>e.slice(1,e.length-1))))||[],o=(null==e?void 0:e.variables)&&Object.keys(e.variables)||[];for(let e of i)o.includes(e)||t({message:`The \`${e}\` variable is not defined in the \`variables\` objects.`,location:r.child(["url"])});for(let e of o)i.includes(e)||t({message:`The \`${e}\` variable is not used in the server's \`url\` field.`,location:r.child(["variables",e]).key(),from:r.child("url")})}})},897:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUnusedComponents=void 0,t.NoUnusedComponents=()=>{let e=new Map;function t(t,r){var n;e.set(t.absolutePointer,{used:(null===(n=e.get(t.absolutePointer))||void 0===n?void 0:n.used)||!1,location:t,name:r})}return{ref(t,{type:r,resolve:n,key:i,location:o}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString(),location:o})}},DefinitionRoot:{leave(t,{report:r}){e.forEach((e=>{e.used||r({message:`Component: "${e.name}" is never used.`,location:e.location.key()})}))}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,n.toString())}},NamedExamples:{Example(e,{location:r,key:n}){t(r,n.toString())}},NamedRequestBodies:{RequestBody(e,{location:r,key:n}){t(r,n.toString())}},NamedHeaders:{Header(e,{location:r,key:n}){t(r,n.toString())}}}}},6350:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;let n=r(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,r,n){var i;e.set(t.absolutePointer,{used:(null===(i=e.get(t.absolutePointer))||void 0===i?void 0:i.used)||!1,componentType:r,name:n})}return{ref:{leave(t,{type:r,resolve:n,key:i}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString()})}}},DefinitionRoot:{leave(t,r){let i=r.getVisitorData();i.removedCount=0,e.forEach((e=>{let{used:r,componentType:o,name:a}=e;if(!r&&o){let e=t.components[o];delete e[a],i.removedCount++,n.isEmptyObject(e)&&delete t.components[o]}})),n.isEmptyObject(t.components)&&delete t.components}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,"schemas",n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,"parameters",n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,"responses",n.toString())}},NamedExamples:{Example(e,{location:r,key:n}){t(r,"examples",n.toString())}},NamedRequestBodies:{RequestBody(e,{location:r,key:n}){t(r,"requestBodies",n.toString())}},NamedHeaders:{Header(e,{location:r,key:n}){t(r,"headers",n.toString())}}}}},3736:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;let n=r(771);t.RequestMimeType=({allowedValues:e})=>({PathMap:{RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}},Callback:{RequestBody(){},Response:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}}}},WebhooksMap:{Response:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}}}})},7557:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;let n=r(771);t.ResponseContainsProperty=e=>{let t,r=e.names||{};return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter(e,r){t=r.key},MediaType:{Schema(e,{report:i,location:o}){var a;if("object"!==e.type)return;let s=r[t]||r[n.getMatchingStatusCodeRange(t)]||r[n.getMatchingStatusCodeRange(t).toLowerCase()]||[];for(let t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||i({message:`Response object must contain a top-level "${t}" property.`,location:o.child("properties").key()})}}}}}}},503:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;let n=r(771);t.ResponseMimeType=({allowedValues:e})=>({PathMap:{Response:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}},Callback:{Response(){},RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}}}},WebhooksMap:{RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}}}})},780:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateExample=t.getSuggest=t.validateDefinedAndNonEmpty=t.fieldNonEmpty=t.missingRequiredField=t.matchesJsonSchemaType=t.oasTypeOf=void 0;let n=r(9991),i=r(7468),o=r(7275);function a(e,t){return`${e} object should contain \`${t}\` field.`}function s(e,t){return`${e} object \`${t}\` must be non-empty string.`}t.oasTypeOf=function(e){return Array.isArray(e)?"array":null===e?"null":typeof e},t.matchesJsonSchemaType=function(e,t,r){if(r&&null===e)return null===e;switch(t){case"array":return Array.isArray(e);case"object":return"object"==typeof e&&null!==e&&!Array.isArray(e);case"null":return null===e;case"integer":return Number.isInteger(e);default:return typeof e===t}},t.missingRequiredField=a,t.fieldNonEmpty=s,t.validateDefinedAndNonEmpty=function(e,t,r){"object"==typeof t&&(void 0===t[e]?r.report({message:a(r.type.name,e),location:r.location.child([e]).key()}):t[e]||r.report({message:s(r.type.name,e),location:r.location.child([e]).key()}))},t.getSuggest=function(e,t){if("string"!=typeof e||!t.length)return[];let r=[];for(let i=0;ie.distance-t.distance)),r.map((e=>e.variant))},t.validateExample=function(e,t,r,{resolve:n,location:a,report:s},l){try{let{valid:c,errors:u}=o.validateJsonSchema(e,t,a.child("schema"),r.pointer,n,l);if(!c)for(let e of u)s({message:`Example value must conform to the schema: ${e.message}.`,location:Object.assign(Object.assign({},new i.Location(r.source,e.instancePath)),{reportOnKey:"additionalProperties"===e.keyword}),from:a,suggest:e.suggest})}catch(e){s({message:`Example validation errored: ${e.message}.`,location:a.child("schema"),from:a})}}},5220:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isNamedType=t.normalizeTypes=t.mapOf=t.listOf=void 0,t.listOf=function(e){return{name:`${e}List`,properties:{},items:e}},t.mapOf=function(e){return{name:`${e}Map`,properties:{},additionalProperties:()=>e}},t.normalizeTypes=function(e,t={}){let r={};for(let t of Object.keys(e))r[t]=Object.assign(Object.assign({},e[t]),{name:t});for(let e of Object.values(r))n(e);return r;function n(e){if(e.additionalProperties&&(e.additionalProperties=i(e.additionalProperties)),e.items&&(e.items=i(e.items)),e.properties){let r={};for(let[n,o]of Object.entries(e.properties))r[n]=i(o),t.doNotResolveExamples&&o&&o.isExample&&(r[n]=Object.assign(Object.assign({},o),{resolvable:!1}));e.properties=r}}function i(e){if("string"==typeof e){if(!r[e])throw Error(`Unknown type name found: ${e}`);return r[e]}return"function"==typeof e?(t,r)=>i(e(t,r)):e&&e.name?(n(e=Object.assign({},e)),e):e&&e.directResolveAs?Object.assign(Object.assign({},e),{directResolveAs:i(e.directResolveAs)}):e}},t.isNamedType=function(e){return"string"==typeof(null==e?void 0:e.name)}},388:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas2Types=void 0;let n=r(5220),i=/^[0-9][0-9Xx]{2}$/,o={properties:{swagger:{type:"string"},info:"Info",host:{type:"string"},basePath:{type:"string"},schemes:{type:"array",items:{type:"string"}},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},paths:"PathMap",definitions:"NamedSchemas",parameters:"NamedParameters",responses:"NamedResponses",securityDefinitions:"NamedSecuritySchemes",security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs"},required:["swagger","paths","info"]},a={properties:{$ref:{type:"string"},parameters:n.listOf("Parameter"),get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation"}},s={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},parameters:n.listOf("Parameter"),responses:"ResponsesMap",schemes:{type:"array",items:{type:"string"}},deprecated:{type:"boolean"},security:n.listOf("SecurityRequirement"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},l={properties:{description:{type:"string"},schema:"Schema",headers:n.mapOf("Header"),examples:"Examples"},required:["description"]},c={properties:{format:{type:"string"},title:{type:"string"},description:{type:"string"},default:null,multipleOf:{type:"number"},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"number"},minLength:{type:"number"},pattern:{type:"string"},maxItems:{type:"number"},minItems:{type:"number"},uniqueItems:{type:"boolean"},maxProperties:{type:"number"},minProperties:{type:"number"},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{type:"string",enum:["object","array","string","number","integer","boolean","null"]},items:e=>Array.isArray(e)?n.listOf("Schema"):"Schema",allOf:n.listOf("Schema"),properties:"SchemaProperties",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",discriminator:{type:"string"},readOnly:{type:"boolean"},xml:"Xml",externalDocs:"ExternalDocs",example:{isExample:!0},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas2Types={DefinitionRoot:o,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License",version:{type:"string"}},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:a,Parameter:{properties:{name:{type:"string"},in:{type:"string",enum:["query","header","path","formData","body"]},description:{type:"string"},required:{type:"boolean"},schema:"Schema",type:{type:"string",enum:["string","number","integer","boolean","array","file"]},format:{type:"string"},allowEmptyValue:{type:"boolean"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&e.in?"body"===e.in?["name","in","schema"]:"array"===e.type?["name","in","type","items"]:["name","in","type"]:["name","in"]},ParameterItems:{properties:{type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},Operation:s,Examples:{properties:{},additionalProperties:{isExample:!0}},Header:{properties:{description:{type:"string"},type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},ResponsesMap:{properties:{default:"Response"},additionalProperties:(e,t)=>i.test(t)?"Response":void 0},Response:l,Schema:c,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},NamedSchemas:n.mapOf("Schema"),NamedResponses:n.mapOf("Response"),NamedParameters:n.mapOf("Parameter"),NamedSecuritySchemes:n.mapOf("SecurityScheme"),SecurityScheme:{properties:{type:{enum:["basic","apiKey","oauth2"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header"]},flow:{enum:["implicit","password","application","accessCode"]},authorizationUrl:{type:"string"},tokenUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","scopes"];case"application":case"password":return["type","flow","tokenUrl","scopes"];default:return["type","flow","scopes"]}default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"basic":return["type","description"];case"apiKey":return["type","name","in","description"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","description","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","description","scopes"];case"application":case"password":return["type","flow","tokenUrl","description","scopes"];default:return["type","flow","tokenUrl","authorizationUrl","description","scopes"]}default:return["type","description"]}},extensionsPrefix:"x-"},XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}}}},5241:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3Types=void 0;let n=r(5220),i=r(7468),o=/^[0-9][0-9Xx]{2}$/,a={properties:{openapi:null,info:"Info",servers:n.listOf("Server"),security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",components:"Components","x-webhooks":"WebhooksMap"},required:["openapi","paths","info"]},s={properties:{url:{type:"string"},description:{type:"string"},variables:n.mapOf("ServerVariable")},required:["url"]},l={properties:{$ref:{type:"string"},servers:n.listOf("Server"),parameters:n.listOf("Parameter"),summary:{type:"string"},description:{type:"string"},get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation",trace:"Operation"}},c={properties:{name:{type:"string"},in:{enum:["query","header","path","cookie"]},description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),content:"MediaTypeMap"},required:["name","in"],requiredOneOf:["schema","content"]},u={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:n.listOf("Parameter"),security:n.listOf("SecurityRequirement"),servers:n.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:n.mapOf("Callback"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},p={properties:{schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),encoding:n.mapOf("Encoding")}},d={properties:{contentType:{type:"string"},headers:n.mapOf("Header"),style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"}}},f={properties:{description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),content:"MediaTypeMap"}},h={properties:{description:{type:"string"},headers:n.mapOf("Header"),content:"MediaTypeMap",links:n.mapOf("Link")},required:["description"]},m={properties:{externalDocs:"ExternalDocs",discriminator:"Discriminator",title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{enum:["object","array","string","number","integer","boolean","null"]},allOf:n.listOf("Schema"),anyOf:n.listOf("Schema"),oneOf:n.listOf("Schema"),not:"Schema",properties:"SchemaProperties",items:e=>Array.isArray(e)?n.listOf("Schema"):"Schema",additionalItems:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},default:null,nullable:{type:"boolean"},readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",example:{isExample:!0},deprecated:{type:"boolean"},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas3Types={DefinitionRoot:a,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},Server:s,ServerVariable:{properties:{enum:{type:"array",items:{type:"string"}},default:{type:"string"},description:null},required:["default"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:l,Parameter:c,Operation:u,Callback:n.mapOf("PathItem"),RequestBody:{properties:{description:{type:"string"},required:{type:"boolean"},content:"MediaTypeMap"},required:["content"]},MediaTypeMap:{properties:{},additionalProperties:"MediaType"},MediaType:p,Example:{properties:{value:{isExample:!0},summary:{type:"string"},description:{type:"string"},externalValue:{type:"string"}}},Encoding:d,Header:f,ResponsesMap:{properties:{default:"Response"},additionalProperties:(e,t)=>o.test(t)?"Response":void 0},Response:h,Link:{properties:{operationRef:{type:"string"},operationId:{type:"string"},parameters:null,requestBody:null,description:{type:"string"},server:"Server"}},Schema:m,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},DiscriminatorMapping:{properties:{},additionalProperties:e=>i.isMappingRef(e)?{type:"string",directResolveAs:"Schema"}:{type:"string"}},Discriminator:{properties:{propertyName:{type:"string"},mapping:"DiscriminatorMapping"},required:["propertyName"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks"}},NamedSchemas:n.mapOf("Schema"),NamedResponses:n.mapOf("Response"),NamedParameters:n.mapOf("Parameter"),NamedExamples:n.mapOf("Example"),NamedRequestBodies:n.mapOf("RequestBody"),NamedHeaders:n.mapOf("Header"),NamedSecuritySchemes:n.mapOf("SecurityScheme"),NamedLinks:n.mapOf("Link"),NamedCallbacks:n.mapOf("Callback"),ImplicitFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},authorizationUrl:{type:"string"}},required:["authorizationUrl","scopes"]},PasswordFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},ClientCredentials:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},AuthorizationCode:{properties:{refreshUrl:{type:"string"},authorizationUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["authorizationUrl","tokenUrl","scopes"]},SecuritySchemeFlows:{properties:{implicit:"ImplicitFlow",password:"PasswordFlow",clientCredentials:"ClientCredentials",authorizationCode:"AuthorizationCode"}},SecurityScheme:{properties:{type:{enum:["apiKey","http","oauth2","openIdConnect"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":return["type","flows","description"];case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"},XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}},WebhooksMap:{properties:{},additionalProperties:()=>"PathItem"}}},2608:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3_1Types=void 0;let n=r(5220),i=r(5241),o={properties:{openapi:null,info:"Info",servers:n.listOf("Server"),security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",webhooks:"WebhooksMap",components:"Components",jsonSchemaDialect:{type:"string"}},required:["openapi","info"],requiredOneOf:["paths","components","webhooks"]},a={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:n.listOf("Parameter"),security:n.listOf("SecurityRequirement"),servers:n.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:n.mapOf("Callback"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}}},s={properties:{$id:{type:"string"},id:{type:"string"},$schema:{type:"string"},definitions:"NamedSchemas",$defs:"NamedSchemas",$vocabulary:{type:"string"},externalDocs:"ExternalDocs",discriminator:"Discriminator",myArbitraryKeyword:{type:"boolean"},title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:e=>Array.isArray(e)?{type:"array",items:{enum:["object","array","string","number","integer","boolean","null"]}}:{enum:["object","array","string","number","integer","boolean","null"]},allOf:n.listOf("Schema"),anyOf:n.listOf("Schema"),oneOf:n.listOf("Schema"),not:"Schema",if:"Schema",then:"Schema",else:"Schema",dependentSchemas:n.listOf("Schema"),prefixItems:n.listOf("Schema"),contains:"Schema",minContains:{type:"integer",minimum:0},maxContains:{type:"integer",minimum:0},patternProperties:{type:"object"},propertyNames:"Schema",unevaluatedItems:"Schema",unevaluatedProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",summary:{type:"string"},properties:"SchemaProperties",items:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},contentEncoding:{type:"string"},contentMediaType:{type:"string"},default:null,readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",examples:{type:"array"},example:{isExample:!0},deprecated:{type:"boolean"},const:null,$comment:{type:"string"},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas3_1Types=Object.assign(Object.assign({},i.Oas3Types),{Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},summary:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},DefinitionRoot:o,Schema:s,License:{properties:{name:{type:"string"},url:{type:"string"},identifier:{type:"string"}},required:["name"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks",pathItems:"NamedPathItems"}},NamedPathItems:n.mapOf("PathItem"),SecurityScheme:{properties:{type:{enum:["apiKey","http","oauth2","openIdConnect","mutualTLS"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":switch(null==e?void 0:e.flows){case"implicit":return["type","flows","authorizationUrl","refreshUrl","description","scopes"];case"password":case"clientCredentials":return["type","flows","tokenUrl","refreshUrl","description","scopes"];default:return["type","flows","authorizationUrl","refreshUrl","tokenUrl","description","scopes"]}case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"},Operation:a})},771:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isCustomRuleId=t.getMatchingStatusCodeRange=t.assignExisting=t.isNotString=t.isString=t.isNotEmptyObject=t.slash=t.isPathParameter=t.readFileAsStringSync=t.isSingular=t.validateMimeTypeOAS3=t.validateMimeType=t.splitCamelCaseIntoWords=t.omitObjectProps=t.pickObjectProps=t.readFileFromUrl=t.isEmptyArray=t.isEmptyObject=t.isPlainObject=t.notUndefined=t.loadYaml=t.popStack=t.pushStack=t.stringifyYaml=t.parseYaml=void 0;let i=r(3197),o=r(4099),a=r(8150),s=r(3450),l=r(5273),c=r(8698);var u=r(5273);function p(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function d(e,t){return t.match(/^https?:\/\//)||(e=e.replace(/^https?:\/\//,"")),o(e,t)}function f(e){return"string"==typeof e}Object.defineProperty(t,"parseYaml",{enumerable:!0,get:function(){return u.parseYaml}}),Object.defineProperty(t,"stringifyYaml",{enumerable:!0,get:function(){return u.stringifyYaml}}),t.pushStack=function(e,t){return{prev:e,value:t}},t.popStack=function(e){var t;return null!==(t=null==e?void 0:e.prev)&&void 0!==t?t:null},t.loadYaml=function(e){return n(this,void 0,void 0,(function*(){let t=yield i.promises.readFile(e,"utf-8");return l.parseYaml(t)}))},t.notUndefined=function(e){return void 0!==e},t.isPlainObject=p,t.isEmptyObject=function(e){return p(e)&&0===Object.keys(e).length},t.isEmptyArray=function(e){return Array.isArray(e)&&0===e.length},t.readFileFromUrl=function(e,t){return n(this,void 0,void 0,(function*(){let r={};for(let n of t.headers)d(e,n.matches)&&(r[n.name]=void 0!==n.envVariable?c.env[n.envVariable]||"":n.value);let n=yield(t.customFetch||a.default)(e,{headers:r});if(!n.ok)throw Error(`Failed to load ${e}: ${n.status} ${n.statusText}`);return{body:yield n.text(),mimeType:n.headers.get("content-type")}}))},t.pickObjectProps=function(e,t){return Object.fromEntries(t.filter((t=>t in e)).map((t=>[t,e[t]])))},t.omitObjectProps=function(e,t){return Object.fromEntries(Object.entries(e).filter((([e])=>!t.includes(e))))},t.splitCamelCaseIntoWords=function(e){let t=e.split(/(?:[-._])|([A-Z][a-z]+)/).filter(Boolean).map((e=>e.toLocaleLowerCase())),r=e.split(/([A-Z]{2,})/).filter((e=>e&&e===e.toUpperCase())).map((e=>e.toLocaleLowerCase()));return new Set([...t,...r])},t.validateMimeType=function({type:e,value:t},{report:r,location:n},i){if(!i)throw Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t[e])for(let o of t[e])i.includes(o)||r({message:`Mime type "${o}" is not allowed`,location:n.child(t[e].indexOf(o)).key()})},t.validateMimeTypeOAS3=function({type:e,value:t},{report:r,location:n},i){if(!i)throw Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t.content)for(let e of Object.keys(t.content))i.includes(e)||r({message:`Mime type "${e}" is not allowed`,location:n.child("content").child(e).key()})},t.isSingular=function(e){return s.isSingular(e)},t.readFileAsStringSync=function(e){return i.readFileSync(e,"utf-8")},t.isPathParameter=function(e){return e.startsWith("{")&&e.endsWith("}")},t.slash=function(e){return/^\\\\\?\\/.test(e)?e:e.replace(/\\/g,"/")},t.isNotEmptyObject=function(e){return!!e&&Object.keys(e).length>0},t.isString=f,t.isNotString=function(e){return!f(e)},t.assignExisting=function(e,t){for(let r of Object.keys(t))e.hasOwnProperty(r)&&(e[r]=t[r])},t.getMatchingStatusCodeRange=e=>`${e}`.replace(/^(\d)\d\d$/,((e,t)=>`${t}XX`)),t.isCustomRuleId=function(e){return e.includes("/")}},8065:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizeVisitors=void 0,t.normalizeVisitors=function(e,t){let r={any:{enter:[],leave:[]}};for(let e of Object.keys(t))r[e]={enter:[],leave:[]};for(let{ruleId:t,severity:n,visitor:o}of(r.ref={enter:[],leave:[]},e))i({ruleId:t,severity:n},o,null);for(let e of Object.keys(r))r[e].enter.sort(((e,t)=>t.depth-e.depth)),r[e].leave.sort(((e,t)=>e.depth-t.depth));return r;function n(e,t,i,o,a=[]){if(a.includes(t))return;a=[...a,t];let s=new Set;for(let r of Object.values(t.properties))r!==i?"object"==typeof r&&null!==r&&r.name&&s.add(r):l(e,a);for(let r of(t.additionalProperties&&"function"!=typeof t.additionalProperties&&(t.additionalProperties===i?l(e,a):void 0!==t.additionalProperties.name&&s.add(t.additionalProperties)),t.items&&(t.items===i?l(e,a):void 0!==t.items.name&&s.add(t.items)),Array.from(s.values())))n(e,r,i,o,a);function l(e,t){for(let n of t.slice(1))r[n.name]=r[n.name]||{enter:[],leave:[]},r[n.name].enter.push(Object.assign(Object.assign({},e),{visit(){},depth:0,context:{isSkippedLevel:!0,seen:new Set,parent:o}}))}}function i(e,o,a,s=0){let l=Object.keys(t);if(0===s)l.push("any"),l.push("ref");else{if(o.any)throw Error("any() is allowed only on top level");if(o.ref)throw Error("ref() is allowed only on top level")}for(let c of l){let l=o[c],u=r[c];if(!l)continue;let p,d,f,h="object"==typeof l;if("ref"===c&&h&&l.skip)throw Error("ref() visitor does not support skip");"function"==typeof l?p=l:h&&(p=l.enter,d=l.leave,f=l.skip);let m={activatedOn:null,type:t[c],parent:a,isSkippedLevel:!1};if("object"==typeof l&&i(e,l,m,s+1),a&&n(e,a.type,t[c],a),p||h){if(p&&"function"!=typeof p)throw Error("DEV: should be function");u.enter.push(Object.assign(Object.assign({},e),{visit:p||(()=>{}),skip:f,depth:s,context:m}))}if(d){if("function"!=typeof d)throw Error("DEV: should be function");u.leave.push(Object.assign(Object.assign({},e),{visit:d,depth:s,context:m}))}}}}},9443:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.walkDocument=void 0;let n=r(7468),i=r(4182),o=r(771),a=r(5220);t.walkDocument=function(e){let{document:t,rootType:r,normalizedVisitors:s,resolvedRefMap:l,ctx:c}=e,u={},p=new Set;!function e(t,r,d,f,h){var m,g,y,v,b,x,w,k,_,O,S;let E=(e,t=A.source.absoluteRef)=>{if(!n.isRef(e))return{location:d,node:e};let r=i.makeRefId(t,e.$ref),o=l.get(r);if(!o)return{location:void 0,node:void 0};let{resolved:a,node:s,document:c,nodePointer:u,error:p}=o;return{location:a?new n.Location(c.source,u):p instanceof i.YamlParseError?new n.Location(p.source,""):void 0,node:s,error:p}},P=d,A=d,{node:$,location:C,error:R}=E(t),j=new Set;if(n.isRef(t)){let e=s.ref.enter;for(let{visit:n,ruleId:i,severity:o,context:a}of e)!p.has(t)&&(j.add(a),n(t,{report:I.bind(void 0,i,o),resolve:E,rawNode:t,rawLocation:P,location:d,type:r,parent:f,key:h,parentLocations:{},oasVersion:c.oasVersion,getVisitorData:N.bind(void 0,i)},{node:$,location:C,error:R}),(null==C?void 0:C.source.absoluteRef)&&c.refTypes&&c.refTypes.set(null==C?void 0:C.source.absoluteRef,r))}if(void 0!==$&&C&&"scalar"!==r.name){A=C;let i=null===(g=null===(m=u[r.name])||void 0===m?void 0:m.has)||void 0===g?void 0:g.call(m,$),l=!1,c=s.any.enter.concat((null===(y=s[r.name])||void 0===y?void 0:y.enter)||[]),p=[];for(let{context:e,visit:n,skip:a,ruleId:s,severity:u}of c)if(e.isSkippedLevel)!e.parent.activatedOn||e.parent.activatedOn.value.nextLevelTypeActivated||e.seen.has(t)||(e.seen.add(t),l=!0,p.push(e));else if(e.parent&&e.parent.activatedOn&&(null===(v=e.activatedOn)||void 0===v?void 0:v.value.withParentNode)!==e.parent.activatedOn.value.node&&(null===(b=e.parent.activatedOn.value.nextLevelTypeActivated)||void 0===b?void 0:b.value)!==r||!e.parent&&!i){p.push(e);let i={node:$,location:C,nextLevelTypeActivated:null,withParentNode:null===(w=null===(x=e.parent)||void 0===x?void 0:x.activatedOn)||void 0===w?void 0:w.value.node,skipped:null!==(O=(null===(_=null===(k=e.parent)||void 0===k?void 0:k.activatedOn)||void 0===_?void 0:_.value.skipped)||(null==a?void 0:a($,h)))&&void 0!==O&&O};e.activatedOn=o.pushStack(e.activatedOn,i);let c=e.parent;for(;c;)c.activatedOn.value.nextLevelTypeActivated=o.pushStack(c.activatedOn.value.nextLevelTypeActivated,r),c=c.parent;if(!i.skipped){l=!0,j.add(e);let{ignoreNextVisitorsOnNode:r}=T(n,$,t,e,s,u);if(r)break}}if(l||!i)if(u[r.name]=u[r.name]||new Set,u[r.name].add($),Array.isArray($)){let t=r.items;if(void 0!==t)for(let r=0;r<$.length;r++)e($[r],t,C.child([r]),$,r)}else if("object"==typeof $&&null!==$){let i=Object.keys(r.properties);for(let o of(r.additionalProperties&&i.push(...Object.keys($).filter((e=>!i.includes(e)))),n.isRef(t)&&i.push(...Object.keys(t).filter((e=>"$ref"!==e&&!i.includes(e)))),i)){let i=$[o],s=C;void 0===i&&(i=t[o],s=d);let l=r.properties[o];void 0===l&&(l=r.additionalProperties),"function"==typeof l&&(l=l(i,o)),!a.isNamedType(l)&&(null==l?void 0:l.directResolveAs)&&(l=l.directResolveAs,i={$ref:i}),l&&void 0===l.name&&!1!==l.resolvable&&(l={name:"scalar",properties:{}}),a.isNamedType(l)&&("scalar"!==l.name||n.isRef(i))&&e(i,l,s.child([o]),$,o)}}let f=s.any.leave,E=((null===(S=s[r.name])||void 0===S?void 0:S.leave)||[]).concat(f);for(let e of p.reverse())if(e.isSkippedLevel)e.seen.delete($);else if(e.activatedOn=o.popStack(e.activatedOn),e.parent){let t=e.parent;for(;t;)t.activatedOn.value.nextLevelTypeActivated=o.popStack(t.activatedOn.value.nextLevelTypeActivated),t=t.parent}for(let{context:e,visit:r,ruleId:n,severity:i}of E)!e.isSkippedLevel&&j.has(e)&&T(r,$,t,e,n,i)}if(A=d,n.isRef(t)){let e=s.ref.leave;for(let{visit:n,ruleId:i,severity:o,context:a}of e)j.has(a)&&n(t,{report:I.bind(void 0,i,o),resolve:E,rawNode:t,rawLocation:P,location:d,type:r,parent:f,key:h,parentLocations:{},oasVersion:c.oasVersion,getVisitorData:N.bind(void 0,i)},{node:$,location:C,error:R})}function T(e,t,n,i,o,a){let s=I.bind(void 0,o,a),l=!1;return e(t,{report:s,resolve:E,rawNode:n,location:A,rawLocation:P,type:r,parent:f,key:h,parentLocations:function(e){var t,r;let n={};for(;e.parent;)(null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.location)&&(n[e.parent.type.name]=null===(r=e.parent.activatedOn)||void 0===r?void 0:r.value.location),e=e.parent;return n}(i),oasVersion:c.oasVersion,ignoreNextVisitorsOnNode(){l=!0},getVisitorData:N.bind(void 0,o)},function(e){var t;let r={};for(;e.parent;)r[e.parent.type.name]=null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.node,e=e.parent;return r}(i),i),{ignoreNextVisitorsOnNode:l}}function I(e,t,r){let n=r.location?Array.isArray(r.location)?r.location:[r.location]:[Object.assign(Object.assign({},A),{reportOnKey:!1})];c.problems.push(Object.assign(Object.assign({ruleId:r.ruleId||e,severity:r.forceSeverity||t},r),{suggest:r.suggest||[],location:n.map((e=>Object.assign(Object.assign(Object.assign({},A),{reportOnKey:!1}),e)))}))}function N(e){return c.visitorsData[e]=c.visitorsData[e]||{},c.visitorsData[e]}}(t.parsed,r,new n.Location(t.source,"#/"),void 0,"")}},5019:function(e,t,r){var n=r(5623);e.exports=function(e){return e?("{}"===e.substr(0,2)&&(e="\\{\\}"+e.substr(2)),function e(t,r){var i=[],o=n("{","}",t);if(!o)return[t];var s=o.pre,l=o.post.length?e(o.post,!1):[""];if(/\$$/.test(o.pre))for(var u=0;u=0;if(!x&&!w)return o.post.match(/,.*\}/)?e(t=o.pre+"{"+o.body+a+o.post):[t];if(x)g=o.body.split(/\.\./);else if(1===(g=function e(t){if(!t)return[""];var r=[],i=n("{","}",t);if(!i)return t.split(",");var o=i.pre,a=i.body,s=i.post,l=o.split(",");l[l.length-1]+="{"+a+"}";var c=e(s);return s.length&&(l[l.length-1]+=c.shift(),l.push.apply(l,c)),r.push.apply(r,l),r}(o.body)).length&&1===(g=e(g[0],!1).map(p)).length)return l.map((function(e){return o.pre+g[0]+e}));if(x){var k,_=c(g[0]),O=c(g[1]),S=Math.max(g[0].length,g[1].length),E=3==g.length?Math.abs(c(g[2])):1,P=f;O<_&&(E*=-1,P=h);var A=g.some(d);y=[];for(var $=_;P($,O);$+=E){if(b)"\\"===(k=String.fromCharCode($))&&(k="");else if(k=String($),A){var C=S-k.length;if(C>0){var R=Array(C+1).join("0");k=$<0?"-"+R+k.slice(1):R+k}}y.push(k)}}else{y=[];for(var j=0;j=t}},5751:function(e){let t="object"==typeof process&&process&&!1;e.exports=t?{sep:"\\"}:{sep:"/"}},4099:function(e,t,r){let n=e.exports=(e,t,r={})=>(g(t),!(!r.nocomment&&"#"===t.charAt(0))&&new v(t,r).match(e));e.exports=n;let i=r(5751);n.sep=i.sep;let o=Symbol("globstar **");n.GLOBSTAR=o;let a=r(5019),s={"!":{open:"(?:(?!(?:",close:"))[^/]*?)"},"?":{open:"(?:",close:")?"},"+":{open:"(?:",close:")+"},"*":{open:"(?:",close:")*"},"@":{open:"(?:",close:")"}},l="[^/]",c="[^/]*?",u=e=>e.split("").reduce(((e,t)=>(e[t]=!0,e)),{}),p=u("().*{}+?[]^$\\!"),d=u("[.("),f=/\/+/;n.filter=(e,t={})=>(r,i,o)=>n(r,e,t);let h=(e,t={})=>{let r={};return Object.keys(e).forEach((t=>r[t]=e[t])),Object.keys(t).forEach((e=>r[e]=t[e])),r};n.defaults=e=>{if(!e||"object"!=typeof e||!Object.keys(e).length)return n;let t=n,r=(r,n,i)=>t(r,n,h(e,i));return(r.Minimatch=class extends t.Minimatch{constructor(t,r){super(t,h(e,r))}}).defaults=r=>t.defaults(h(e,r)).Minimatch,r.filter=(r,n)=>t.filter(r,h(e,n)),r.defaults=r=>t.defaults(h(e,r)),r.makeRe=(r,n)=>t.makeRe(r,h(e,n)),r.braceExpand=(r,n)=>t.braceExpand(r,h(e,n)),r.match=(r,n,i)=>t.match(r,n,h(e,i)),r},n.braceExpand=(e,t)=>m(e,t);let m=(e,t={})=>(g(e),t.nobrace||!/\{(?:(?!\{).)*\}/.test(e)?[e]:a(e)),g=e=>{if("string"!=typeof e)throw TypeError("invalid pattern");if(e.length>65536)throw TypeError("pattern is too long")},y=Symbol("subparse");n.makeRe=(e,t)=>new v(e,t||{}).makeRe(),n.match=(e,t,r={})=>{let n=new v(t,r);return e=e.filter((e=>n.match(e))),n.options.nonull&&!e.length&&e.push(t),e};class v{constructor(e,t){g(e),t||(t={}),this.options=t,this.set=[],this.pattern=e,this.regexp=null,this.negate=!1,this.comment=!1,this.empty=!1,this.partial=!!t.partial,this.make()}debug(){}make(){let e=this.pattern,t=this.options;if(!t.nocomment&&"#"===e.charAt(0))return void(this.comment=!0);if(!e)return void(this.empty=!0);this.parseNegate();let r=this.globSet=this.braceExpand();t.debug&&(this.debug=(...e)=>console.error(...e)),this.debug(this.pattern,r),r=this.globParts=r.map((e=>e.split(f))),this.debug(this.pattern,r),r=r.map(((e,t,r)=>e.map(this.parse,this))),this.debug(this.pattern,r),r=r.filter((e=>-1===e.indexOf(!1))),this.debug(this.pattern,r),this.set=r}parseNegate(){if(this.options.nonegate)return;let e=this.pattern,t=!1,r=0;for(let n=0;n>> no match, partial?",e,d,t,f),d!==s))}if("string"==typeof u?(c=p===u,this.debug("string match",u,p,c)):(c=p.match(u),this.debug("pattern match",u,p,c)),!c)return!1}if(i===s&&a===l)return!0;if(i===s)return r;if(a===l)return i===s-1&&""===e[i];throw Error("wtf?")}braceExpand(){return m(this.pattern,this.options)}parse(e,t){g(e);let r=this.options;if("**"===e){if(!r.noglobstar)return o;e="*"}if(""===e)return"";let n,i,a,u,f="",h=!!r.nocase,m=!1,v=[],b=[],x=!1,w=-1,k=-1,_="."===e.charAt(0)?"":r.dot?"(?!(?:^|\\/)\\.{1,2}(?:$|\\/))":"(?!\\.)",O=()=>{if(n){switch(n){case"*":f+=c,h=!0;break;case"?":f+=l,h=!0;break;default:f+="\\"+n}this.debug("clearStateChar %j %j",n,f),n=!1}};for(let t,o=0;o(r||(r="\\"),t+t+r+"|"))),this.debug("tail=%j\n %s",e,e,a,f);let t="*"===a.type?c:"?"===a.type?l:"\\"+a.type;h=!0,f=f.slice(0,a.reStart)+t+"\\("+e}O(),m&&(f+="\\\\");let S=d[f.charAt(0)];for(let e=b.length-1;e>-1;e--){let r=b[e],n=f.slice(0,r.reStart),i=f.slice(r.reStart,r.reEnd-8),o=f.slice(r.reEnd),a=f.slice(r.reEnd-8,r.reEnd)+o,s=n.split("(").length-1,l=o;for(let e=0;e((e=e.map((e=>"string"==typeof e?e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"):e===o?o:e._src)).reduce(((e,t)=>(e[e.length-1]===o&&t===o||e.push(t),e)),[])).forEach(((t,n)=>{t===o&&e[n-1]!==o&&(0===n?e.length>1?e[n+1]="(?:\\/|"+r+"\\/)?"+e[n+1]:e[n]=r:n===e.length-1?e[n-1]+="(?:\\/|"+r+")?":(e[n-1]+="(?:\\/|\\/"+r+"\\/)"+e[n+1],e[n+1]=o))})),e.filter((e=>e!==o)).join("/")))).join("|");i="^(?:"+i+")$",this.negate&&(i="^(?!"+i+").*$");try{this.regexp=RegExp(i,n)}catch(e){this.regexp=!1}return this.regexp}match(e,t=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return""===e;if("/"===e&&t)return!0;let r=this.options;"/"!==i.sep&&(e=e.split(i.sep).join("/")),e=e.split(f),this.debug(this.pattern,"split",e);let n,o=this.set;this.debug(this.pattern,"set",o);for(let t=e.length-1;t>=0&&!(n=e[t]);t--);for(let i=0;i=0&&c>0){if(e===t)return[l,c];for(n=[],o=r.length;u>=0&&!s;)u==l?(n.push(u),l=r.indexOf(e,u+1)):1==n.length?s=[n.pop(),c]:((i=n.pop())=0?l:c;n.length&&(s=[o,a])}return s}e.exports=t,t.range=n},4480:function(e,t,r){"use strict";var n=r.g.process&&process.nextTick||r.g.setImmediate||function(e){setTimeout(e,0)};e.exports=function(e,t){return e?void t.then((function(t){n((function(){e(null,t)}))}),(function(t){n((function(){e(t)}))})):t}},4184:function(e,t){var r;!function(){"use strict";var n={}.hasOwnProperty;function i(){for(var e=[],t=0;tu;)if((s=l[u++])!=s)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===r)return e||u||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},2092:function(e,t,r){var n=r(9974),i=r(8361),o=r(7908),a=r(7466),s=r(5417),l=[].push,c=function(e){var t=1==e,r=2==e,c=3==e,u=4==e,p=6==e,d=7==e,f=5==e||p;return function(h,m,g,y){for(var v,b,x=o(h),w=i(x),k=n(m,g,3),_=a(w.length),O=0,S=y||s,E=t?S(h,_):r||d?S(h,0):void 0;_>O;O++)if((f||O in w)&&(b=k(v=w[O],O,x),e))if(t)E[O]=b;else if(b)switch(e){case 3:return!0;case 5:return v;case 6:return O;case 2:l.call(E,v)}else switch(e){case 4:return!1;case 7:l.call(E,v)}return p?-1:c||u?u:E}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterOut:c(7)}},1194:function(e,t,r){var n=r(7293),i=r(5112),o=r(7392),a=i("species");e.exports=function(e){return o>=51||!n((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},5417:function(e,t,r){var n=r(111),i=r(3157),o=r(5112)("species");e.exports=function(e,t){var r;return i(e)&&("function"!=typeof(r=e.constructor)||r!==Array&&!i(r.prototype)?n(r)&&null===(r=r[o])&&(r=void 0):r=void 0),new(void 0===r?Array:r)(0===t?0:t)}},4326:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},648:function(e,t,r){var n=r(1694),i=r(4326),o=r(5112)("toStringTag"),a="Arguments"==i(function(){return arguments}());e.exports=n?i:function(e){var t,r,n;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(r=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?r:a?i(t):"Object"==(n=i(t))&&"function"==typeof t.callee?"Arguments":n}},9920:function(e,t,r){var n=r(6656),i=r(3887),o=r(1236),a=r(3070);e.exports=function(e,t){for(var r=i(t),s=a.f,l=o.f,c=0;c=74)&&(n=a.match(/Chrome\/(\d+)/))&&(i=n[1]),e.exports=i&&+i},748:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(e,t,r){var n=r(7854),i=r(1236).f,o=r(8880),a=r(1320),s=r(3505),l=r(9920),c=r(4705);e.exports=function(e,t){var r,u,p,d,f,h=e.target,m=e.global,g=e.stat;if(r=m?n:g?n[h]||s(h,{}):(n[h]||{}).prototype)for(u in t){if(d=t[u],p=e.noTargetGet?(f=i(r,u))&&f.value:r[u],!c(m?u:h+(g?".":"#")+u,e.forced)&&void 0!==p){if(typeof d==typeof p)continue;l(d,p)}(e.sham||p&&p.sham)&&o(d,"sham",!0),a(r,u,d,e)}}},7293:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},9974:function(e,t,r){var n=r(3099);e.exports=function(e,t,r){if(n(e),void 0===t)return e;switch(r){case 0:return function(){return e.call(t)};case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,i){return e.call(t,r,n,i)}}return function(){return e.apply(t,arguments)}}},5005:function(e,t,r){var n=r(857),i=r(7854),o=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(n[e])||o(i[e]):n[e]&&n[e][t]||i[e]&&i[e][t]}},7854:function(e,t,r){var n=function(e){return e&&e.Math==Math&&e};e.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof r.g&&r.g)||function(){return this}()||Function("return this")()},6656:function(e,t,r){var n=r(7908),i={}.hasOwnProperty;e.exports=Object.hasOwn||function(e,t){return i.call(n(e),t)}},3501:function(e){e.exports={}},490:function(e,t,r){var n=r(5005);e.exports=n("document","documentElement")},4664:function(e,t,r){var n=r(9781),i=r(7293),o=r(317);e.exports=!n&&!i((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},8361:function(e,t,r){var n=r(7293),i=r(4326),o="".split;e.exports=n((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==i(e)?o.call(e,""):Object(e)}:Object},2788:function(e,t,r){var n=r(5465),i=Function.toString;"function"!=typeof n.inspectSource&&(n.inspectSource=function(e){return i.call(e)}),e.exports=n.inspectSource},9909:function(e,t,r){var n,i,o,a=r(8536),s=r(7854),l=r(111),c=r(8880),u=r(6656),p=r(5465),d=r(6200),f=r(3501),h="Object already initialized",m=s.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),y=g.get,v=g.has,b=g.set;n=function(e,t){if(v.call(g,e))throw TypeError(h);return t.facade=e,b.call(g,e,t),t},i=function(e){return y.call(g,e)||{}},o=function(e){return v.call(g,e)}}else{var x=d("state");f[x]=!0,n=function(e,t){if(u(e,x))throw TypeError(h);return t.facade=e,c(e,x,t),t},i=function(e){return u(e,x)?e[x]:{}},o=function(e){return u(e,x)}}e.exports={set:n,get:i,has:o,enforce:function(e){return o(e)?i(e):n(e,{})},getterFor:function(e){return function(t){var r;if(!l(t)||(r=i(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return r}}}},3157:function(e,t,r){var n=r(4326);e.exports=Array.isArray||function(e){return"Array"==n(e)}},4705:function(e,t,r){var n=r(7293),i=/#|\.prototype\./,o=function(e,t){var r=s[a(e)];return r==c||r!=l&&("function"==typeof t?n(t):!!t)},a=o.normalize=function(e){return String(e).replace(i,".").toLowerCase()},s=o.data={},l=o.NATIVE="N",c=o.POLYFILL="P";e.exports=o},111:function(e){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},1913:function(e){e.exports=!1},133:function(e,t,r){var n=r(7392),i=r(7293);e.exports=!!Object.getOwnPropertySymbols&&!i((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(e,t,r){var n=r(7854),i=r(2788),o=n.WeakMap;e.exports="function"==typeof o&&/native code/.test(i(o))},30:function(e,t,r){var n,i=r(9670),o=r(6048),a=r(748),s=r(3501),l=r(490),c=r(317),u=r(6200)("IE_PROTO"),p=function(){},d=function(e){return" + Stremio-Jackett + + + + +
+
+ logo +
+

Stremio-Jackett Rework

+

+ Addon configuration

+ +
+

Streaming

+

You will enter information + about streaming here

+
+
+
+ +
+
+ +

Enable debrid service for faster streaming

+
+
+
+
+ +
+
+ +

(ATTENTION: this is illegal in some + countries)

+
+
+
+ +
+

Torrent Providers

+

You will enter information about + your torrent providers here

+
+
+
+ +
+
+ +

Enable Jackett for more results

+
+
+
+
+ +
+
+ +

Enable caching for faster results

+
+
+
+
+ +
+
+ +

Zilean's api - DMM hashlist scraper for more + results

+
+
+
+ +
+

Redis Information

+

You will enter information about your Redis server here

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ + +
+

Jackett Information

+

You will enter information about + your + Jackett server here

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+

Debrid Information

+

You will enter your debrid + provider + and its API key here

+ +
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+

Metadata Provider

+

You will enter information about + your metadata provider here

+
+
+
+ +
+
+ +

Use TMDB for more accurate metadata in different + languages

+
+
+
+
+ +
+
+ +

Use Cinemeta for simple metadata

+
+
+
+ +
+

TMDB Informations

+

You will enter information + about TMDB here. You can + find your API key in "Settings" after registering here

+ +
+
+ +
+ +
+
+
+
+ +
+

Zilean Cache + DMM API

+

Please fill your Zilean DMM API + Cache + informations here.

+ +
+
+ +
+ +
+
+
+
+ +
+

Shared Public Cache

+

Please fill your Public + Cache URL + informations here.

+ +
+
+ +
+ +
+
+
+
+ +
+

Filtering

+

Set-up here your filtering + parameters. Suiting results + is a must :)

+ +
+
+ Sorting + +

Choose the sorting that + suits + you the best.

+ +
+
+
+ +
+
+ +

Get the best quality on top

+
+
+
+
+ +
+
+ +

Filter results by size descending +

+
+
+
+
+ +
+
+ +

Filter results by size ascending

+
+
+
+
+ +
+
+ +

Filter results by quality then size + descending

+
+
+
+
+ +
+ Quality + Exclusion + +

Check qualities that you + want + to exclude

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+
+
+
+
+ +

Choose your languages

+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + +
+
+
+
+
+ +

Set a maximum size + for + your results in + GB.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + maximum + results you want + per + quality.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + maximum + results you + want.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + minimum cached results you want befor a new search.

+ +
+ +
+
+ +
+
+
+ +
+ Install + Copy +
+
+
+
+ + + + \ No newline at end of file diff --git a/stream_fusion/utils/cache/__init__.py b/stream_fusion/utils/cache/__init__.py new file mode 100644 index 0000000..0f11285 --- /dev/null +++ b/stream_fusion/utils/cache/__init__.py @@ -0,0 +1,4 @@ +from stream_fusion.utils.cache.cache_base import CacheBase +from stream_fusion.utils.cache.local_redis import RedisCache + +__all__ = ["CacheBase", "RedisCache"] \ No newline at end of file diff --git a/stream_fusion/utils/cache/cache.py b/stream_fusion/utils/cache/cache.py new file mode 100644 index 0000000..dc85947 --- /dev/null +++ b/stream_fusion/utils/cache/cache.py @@ -0,0 +1,142 @@ +import os +import json +import copy +import redis +import pickle +import hashlib +import requests + +from typing import List + +from stream_fusion.logging_config import logger +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.constants import CACHER_URL, EXCLUDED_TRACKERS +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + +# Redis connection initialization +redis_client = redis.Redis(host="redis", port=6379) + +CACHE_EXPIRATION_TIME = 48 * 60 * 60 + +def cache_key(media): + if isinstance(media, Movie): + key_string = f"movie:{media.titles[0]}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"series:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode('utf-8')).hexdigest() + return hashed_key[:16] + +def search_redis(media): + try: + logger.info(f"Searching for cached {media.type} results") + hash_key = cache_key(media) + cached_result = redis_client.hgetall(hash_key) + + if cached_result: + return [pickle.loads(value) for value in cached_result.values()] + else: + return [] + except Exception as e: + logger.error(f"Error in search_cache: {str(e)}") + return [] + +def cache_redis(torrents: List[JackettResult], media): + if os.getenv("NODE_ENV") == "development": + return + + logger.info("Started caching results") + + cached_torrents = {} + for index, torrent in enumerate(torrents): + try: + cached_torrent = copy.deepcopy(torrent) + cached_torrent.indexer = "Locally cached" + cached_torrents[str(index)] = pickle.dumps(cached_torrent) + except Exception as e: + logger.error(f"Failed to create cached copy of torrent: {str(e)}") + + try: + hash_key = cache_key(media) + redis_client.hmset(hash_key, cached_torrents) + redis_client.expire(hash_key, CACHE_EXPIRATION_TIME) + + logger.info(f"Cached {len(cached_torrents)} {media.type} results") + except Exception as e: + logger.error(f"Failed to cache results: {str(e)}") + + +def search_public(media): + logger.info("Searching for public cached " + media.type + " results") + url = CACHER_URL + "getResult/" + media.type + "/" + # Without that, the cache doesn't return results. Maybe make multiple requests? One for each language, just like jackett? + cache_search = media.__dict__ + cache_search["title"] = cache_search["titles"][0] + cache_search["language"] = cache_search["languages"][0] + # TODO: Wtf, why do we need to use __dict__ here? And also, why is it stuck when we use media directly? + response = requests.get(url, json=cache_search) + return response.json() + + +def cache_public(torrents: List[TorrentItem], media): + if os.getenv("NODE_ENV") == "development": + return + + logger.info("Started Public Caching results") + + cache_items = [] + for torrent in torrents: + if torrent.indexer in EXCLUDED_TRACKERS: + continue + + try: + cache_item = dict() + + cache_item["title"] = torrent.raw_title + cache_item["trackers"] = "tracker:".join(torrent.trackers) + cache_item["magnet"] = torrent.magnet + cache_item["files"] = [] # I guess keep it empty? + cache_item["hash"] = torrent.info_hash + cache_item["indexer"] = torrent.indexer + cache_item["quality"] = ( + torrent.parsed_data.resolution[0] + if len(torrent.parsed_data.resolution) > 0 + else "Unknown" + ) + cache_item["qualitySpec"] = ";".join(torrent.parsed_data.quality) + cache_item["seeders"] = torrent.seeders + cache_item["size"] = torrent.size + cache_item["language"] = ";".join(torrent.languages) + cache_item["type"] = media.type + cache_item["availability"] = torrent.availability + + if media.type == "movie": + cache_item["year"] = media.year + elif media.type == "series": + cache_item["season"] = media.season + cache_item["episode"] = media.episode + cache_item["seasonfile"] = ( + False # I guess keep it false to not mess up results? + ) + + cache_items.append(cache_item) + except: + logger.exception("An exception occured durring public cache parsing") + pass + + try: + url = f"{CACHER_URL}pushResult/{media.type}" + cache_data = json.dumps(cache_items, indent=4) + response = requests.post(url, data=cache_data) + response.raise_for_status() + + if response.status_code == 200: + logger.info(f"Cached {str(len(cache_items))} {media.type} results on Public Cache") + else: + logger.error(f"Failed to public cache {media.type} results: {str(response)}") + except: + logger.error("Failed to public cache results") + pass diff --git a/stream_fusion/utils/cache/cache_base.py b/stream_fusion/utils/cache/cache_base.py new file mode 100644 index 0000000..480af1e --- /dev/null +++ b/stream_fusion/utils/cache/cache_base.py @@ -0,0 +1,85 @@ +import hashlib +from abc import ABC, abstractmethod +from typing import Any, Callable + +from stream_fusion.logging_config import logger + +class CacheBase(ABC): + def __init__(self, config: dict): + """ + Initialize the cache base class. + Args: + config (dict): Configuration dictionary for the cache. + """ + self.logger = logger + self.config = config + + @abstractmethod + def can_cache(self) -> bool: + """ + Check if caching is possible. + Returns: + bool: True if caching is possible, False otherwise. + """ + pass + + @abstractmethod + def get(self, key: str) -> Any: + """ + Retrieve a value from the cache. + Args: + key (str): The cache key. + Returns: + Any: The cached value if found, None otherwise. + """ + pass + + @abstractmethod + def set(self, key: str, value: Any) -> None: + """ + Store a value in the cache. + Args: + key (str): The cache key. + value (Any): The value to be cached. + """ + pass + + def __call__(self, func: Callable) -> Callable: + """ + Decorator for caching function results. + Args: + func (Callable): The function to be cached. + Returns: + Callable: The wrapped function with caching functionality. + """ + def wrapper(*args, **kwargs): + if not self.can_cache(): + self.logger.error("Cache is not available or cannot be used.") + return func(*args, **kwargs) + + key = self.generate_key(func.__name__, *args, **kwargs) + cached_result = self.get(key) + + if cached_result is not None: + self.logger.info(f"Result found in cache for key: {key}") + return cached_result + + result = func(*args, **kwargs) + self.set(key, result) + return result + + return wrapper + + def generate_key(self, func_name: str, *args, **kwargs) -> str: + """ + Generate a cache key based on function name and arguments. + Args: + func_name (str): Name of the function being cached. + *args: Positional arguments of the function. + **kwargs: Keyword arguments of the function. + Returns: + str: A hashed key string. + """ + arg_string = f"{func_name}:{str(args)}:{str(kwargs)}" + hashed_key = hashlib.sha256(arg_string.encode("utf-8")).hexdigest() + return hashed_key[:16] \ No newline at end of file diff --git a/stream_fusion/utils/cache/local_redis.py b/stream_fusion/utils/cache/local_redis.py new file mode 100644 index 0000000..3ccb1f6 --- /dev/null +++ b/stream_fusion/utils/cache/local_redis.py @@ -0,0 +1,219 @@ +import asyncio +import inspect +import jsonpickle +import time +from typing import Any, List +import hashlib +import redis +from stream_fusion.settings import settings +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.cache.cache_base import CacheBase + +class RedisCache(CacheBase): + def __init__(self, config): + super().__init__(config) + self.redis_host = settings.redis_host + self.redis_port = settings.redis_port + self.redis_url = f"redis://{self.redis_host}:{self.redis_port}" + self._redis_client = None + self.media_expiration = settings.redis_expiration + + def get_redis_client(self): + if not self._redis_client: + try: + self._redis_client = redis.Redis.from_url( + self.redis_url, + max_connections=10 + ) + except redis.ConnectionError: + self.logger.error("Failed to create Redis client") + self._redis_client = None + return self._redis_client + + def get_list(self, key: str) -> List[Any]: + result = self.get(key) + if isinstance(result, list): + return result + elif result is not None: + return [result] + return None + + def generate_key(self, func_name: str, *args, **kwargs) -> str: + media = kwargs.get('media') + if media is None and args: + media = next((arg for arg in args if isinstance(arg, (Movie, Series))), None) + + if media: + if isinstance(media, Movie): + key_string = f"movie:{media.titles[0]}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"series:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + else: + key_string = f"{func_name}:{str(args)}:{str(kwargs)}" + + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + def clear(self) -> None: + try: + client = self.get_redis_client() + client.flushdb() + self.logger.info("Cache cleared successfully") + except Exception as e: + self.logger.error(f"Error clearing cache: {e}") + + def get_or_set(self, func: callable, *args, **kwargs) -> Any: + self.logger.debug(f"Entering get_or_set for function: {func.__name__}") + start_time = time.time() + + if not self.can_cache(): + self.logger.debug("Cache is not available, executing function directly") + return self._execute_func(func, *args, **kwargs) + + key = self.generate_key(func.__name__, *args, **kwargs) + self.logger.debug(f"Generated cache key: {key}") + + cached_result = self.get(key) + self.logger.debug(f"Attempted to get result from cache. Found: {cached_result is not None}") + + if cached_result is not None: + self.logger.debug(f"Returning cached result for key: {key}") + return cached_result + + self.logger.debug(f"Cache miss for key: {key}. Executing function.") + result = self._execute_func(func, *args, **kwargs) + self.logger.debug(f"Function execution completed. Setting result in cache.") + self.set(key, result) + + end_time = time.time() + self.logger.debug(f"get_or_set completed in {end_time - start_time:.2f} seconds") + return result + + def _execute_func(self, func, *args, **kwargs): + self.logger.debug(f"Executing function: {func.__name__}") + start_time = time.time() + + sig = inspect.signature(func) + params = sig.parameters + + self.logger.debug(f"Function {func.__name__} expects {len(params)} arguments") + self.logger.debug(f"Received args: {args}, kwargs: {kwargs}") + + call_args = {} + for i, (param_name, param) in enumerate(params.items()): + if i < len(args): + call_args[param_name] = args[i] + elif param_name in kwargs: + call_args[param_name] = kwargs[param_name] + elif param.default is not param.empty: + call_args[param_name] = param.default + else: + self.logger.error(f"Missing required argument: {param_name}") + raise TypeError(f"Missing required argument: {param_name}") + + self.logger.debug(f"Prepared arguments for {func.__name__}: {call_args}") + + if asyncio.iscoroutinefunction(func): + self.logger.debug(f"Function {func.__name__} is asynchronous") + loop = asyncio.get_event_loop() + try: + result = loop.run_until_complete(func(**call_args)) + self.logger.debug(f"Asynchronous function {func.__name__} completed successfully") + except Exception as e: + self.logger.error(f"Error executing asynchronous function {func.__name__}: {str(e)}") + raise + else: + self.logger.debug(f"Function {func.__name__} is synchronous") + try: + result = func(**call_args) + self.logger.debug(f"Synchronous function {func.__name__} completed successfully") + except Exception as e: + self.logger.error(f"Error executing synchronous function {func.__name__}: {str(e)}") + raise + + end_time = time.time() + self.logger.debug(f"Function {func.__name__} executed in {end_time - start_time:.2f} seconds") + return result + + def can_cache(self) -> bool: + self.logger.debug("Checking if caching is possible") + try: + client = self.get_redis_client() + result = client.ping() + self.logger.debug(f"Redis ping result: {result}") + return result + except redis.ConnectionError: + self.logger.error("Unable to connect to Redis") + return False + + def get(self, key: str) -> Any: + self.logger.debug(f"Attempting to get value for key: {key}") + try: + client = self.get_redis_client() + cached_result = client.get(key) + if cached_result: + self.logger.debug(f"Value found in cache for key: {key}") + return jsonpickle.decode(cached_result) + self.logger.debug(f"No value found in cache for key: {key}") + return None + except Exception as e: + self.logger.error(f"Error retrieving from Redis: {e}") + return None + + def set(self, key: str, value: Any, expiration: int = None) -> None: + self.logger.debug(f"Attempting to set value for key: {key}") + if expiration is None: + expiration = self.media_expiration + + try: + client = self.get_redis_client() + cached_data = jsonpickle.encode(value) + result = client.set(key, cached_data, ex=expiration) + self.logger.debug(f"Set operation result for key {key}: {result}") + except Exception as e: + self.logger.error(f"Error storing in Redis: {e}") + + def delete(self, key: str) -> bool: + try: + client = self.get_redis_client() + return bool(client.delete(key)) + except Exception as e: + self.logger.error(f"Error deleting key from Redis: {e}") + return False + + def exists(self, key: str) -> bool: + try: + client = self.get_redis_client() + return bool(client.exists(key)) + except Exception as e: + self.logger.error(f"Error checking key existence in Redis: {e}") + return False + + def get_ttl(self, key: str) -> int: + try: + client = self.get_redis_client() + return client.ttl(key) + except Exception as e: + self.logger.error(f"Error getting TTL from Redis: {e}") + return -1 + + def update_expiration(self, key: str, expiration: int) -> bool: + try: + client = self.get_redis_client() + return bool(client.expire(key, expiration)) + except Exception as e: + self.logger.error(f"Error updating expiration in Redis: {e}") + return False + + def close(self): + if self._redis_client: + self._redis_client.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() diff --git a/stream_fusion/utils/debrid/__init__.py b/stream_fusion/utils/debrid/__init__.py new file mode 100644 index 0000000..764eb18 --- /dev/null +++ b/stream_fusion/utils/debrid/__init__.py @@ -0,0 +1,9 @@ +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.debrid.realdebrid import RealDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service + + + +__all__ = ["AllDebrid", "BaseDebrid", "RealDebrid", "Premiumize", "get_debrid_service"] \ No newline at end of file diff --git a/stream_fusion/utils/debrid/alldebrid.py b/stream_fusion/utils/debrid/alldebrid.py new file mode 100644 index 0000000..1c2cc40 --- /dev/null +++ b/stream_fusion/utils/debrid/alldebrid.py @@ -0,0 +1,126 @@ +# alldebrid.py +import json +import uuid +from urllib.parse import unquote + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + + +class AllDebrid(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://api.alldebrid.com/v4/" + + def add_magnet(self, magnet, ip): + url = f"{self.base_url}magnet/upload?agent=jackett&apikey={self.config['debridKey']}&magnet={magnet}&ip={ip}" + return self.get_json_response(url) + + def add_torrent(self, torrent_file, ip): + url = f"{self.base_url}magnet/upload/file?agent=jackett&apikey={self.config['debridKey']}&ip={ip}" + files = {"files[0]": (str(uuid.uuid4()) + ".torrent", torrent_file, 'application/x-bittorrent')} + return self.get_json_response(url, method='post', files=files) + + def check_magnet_status(self, id, ip): + url = f"{self.base_url}magnet/status?agent=jackett&apikey={self.config['debridKey']}&id={id}&ip={ip}" + return self.get_json_response(url) + + def unrestrict_link(self, link, ip): + url = f"{self.base_url}link/unlock?agent=jackett&apikey={self.config['debridKey']}&link={link}&ip={ip}" + return self.get_json_response(url) + + def get_stream_link(self, query_string, config, ip): + query = json.loads(query_string) + + magnet = query['magnet'] + stream_type = query['type'] + torrent_download = unquote(query["torrent_download"]) if query["torrent_download"] is not None else None + + torrent_id = self.__add_magnet_or_torrent(magnet, torrent_download, ip) + logger.info(f"Torrent ID: {torrent_id}") + + if not self.wait_for_ready_status( + lambda: self.check_magnet_status(torrent_id, ip)["data"]["magnets"]["status"] == "Ready"): + logger.error("Torrent not ready, caching in progress.") + return NO_CACHE_VIDEO_URL + logger.info("Torrent is ready.") + + logger.info(f"Getting data for torrent id: {torrent_id}") + data = self.check_magnet_status(torrent_id, ip)["data"] + logger.info(f"Retrieved data for torrent id") + + link = NO_CACHE_VIDEO_URL + if stream_type == "movie": + logger.info("Getting link for movie") + link = max(data["magnets"]['links'], key=lambda x: x['size'])['link'] + elif stream_type == "series": + season = query['season'] + episode = query['episode'] + logger.info(f"Getting link for series {season}, {episode}") + + matching_files = [] + for file in data["magnets"]["links"]: + if season_episode_in_filename(file["filename"], season, episode): + matching_files.append(file) + + if len(matching_files) == 0: + logger.error(f"No matching files for {season} {episode} in torrent.") + return f"Error: No matching files for {season} {episode} in torrent." + + link = max(matching_files, key=lambda x: x["size"])["link"] + else: + logger.error("Unsupported stream type.") + return "Error: Unsupported stream type." + + if link == NO_CACHE_VIDEO_URL: + return link + + logger.info(f"Alldebrid link: {link}") + + unlocked_link_data = self.unrestrict_link(link, ip) + + if not unlocked_link_data: + logger.error("Failed to unlock link.") + return "Error: Failed to unlock link." + + logger.info(f"Unrestricted link: {unlocked_link_data['data']['link']}") + + return unlocked_link_data["data"]["link"] + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + if len(hashes_or_magnets) == 0: + logger.info("No hashes to be sent to All-Debrid.") + return dict() + + url = f"{self.base_url}magnet/instant?agent=jackett&apikey={self.config['debridKey']}&magnets[]={'&magnets[]='.join(hashes_or_magnets)}&ip={ip}" + return self.get_json_response(url) + + def __add_magnet_or_torrent(self, magnet, torrent_download=None, ip=None): + torrent_id = "" + if torrent_download is None: + logger.info(f"Adding magnet to AllDebrid") + magnet_response = self.add_magnet(magnet, ip) + logger.info(f"AllDebrid add magnet response: {magnet_response}") + + if not magnet_response or "status" not in magnet_response or magnet_response["status"] != "success": + return "Error: Failed to add magnet." + + torrent_id = magnet_response["data"]["magnets"][0]["id"] + else: + logger.info(f"Downloading torrent file from Jackett") + torrent_file = self.donwload_torrent_file(torrent_download) + logger.info(f"Torrent file downloaded from Jackett") + + logger.info(f"Adding torrent file to AllDebrid") + upload_response = self.add_torrent(torrent_file, ip) + logger.info(f"AllDebrid add torrent file response: {upload_response}") + + if not upload_response or "status" not in upload_response or upload_response["status"] != "success": + return "Error: Failed to add torrent file." + + torrent_id = upload_response["data"]["files"][0]["id"] + + logger.info(f"New torrent ID: {torrent_id}") + return torrent_id diff --git a/stream_fusion/utils/debrid/base_debrid.py b/stream_fusion/utils/debrid/base_debrid.py new file mode 100644 index 0000000..a0c065a --- /dev/null +++ b/stream_fusion/utils/debrid/base_debrid.py @@ -0,0 +1,61 @@ +import time + +import requests + +from stream_fusion.logging_config import logger + + +class BaseDebrid: + def __init__(self, config): + self.config = config + self.logger = logger + self.__session = requests.Session() + + def get_json_response(self, url, method='get', data=None, headers=None, files=None): + if method == 'get': + response = self.__session.get(url, headers=headers) + elif method == 'post': + response = self.__session.post(url, data=data, headers=headers, files=files) + elif method == 'put': + response = self.__session.put(url, data=data, headers=headers) + elif method == 'delete': + response = self.__session.delete(url, headers=headers) + else: + raise ValueError(f"Unsupported HTTP method: {method}") + + # Check if the request was successful + if response.ok: + try: + return response.json() + except ValueError: + self.logger.error(f"Failed to parse response as JSON: {response.text}") + return None + else: + self.logger.error(f"Request failed with status code {response.status_code}") + return None + + def wait_for_ready_status(self, check_status_func, timeout=30, interval=5): + self.logger.info(f"Waiting for {timeout} seconds to cache.") + start_time = time.time() + while time.time() - start_time < timeout: + if check_status_func(): + self.logger.info("File is ready!") + return True + time.sleep(interval) + self.logger.info(f"Waiting timed out.") + return False + + def donwload_torrent_file(self, download_url): + response = requests.get(download_url) + response.raise_for_status() + + return response.content + + def get_stream_link(self, query, ip=None): + raise NotImplementedError + + def add_magnet(self, magnet, ip=None): + raise NotImplementedError + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + raise NotImplementedError diff --git a/stream_fusion/utils/debrid/get_debrid_service.py b/stream_fusion/utils/debrid/get_debrid_service.py new file mode 100644 index 0000000..a0fa877 --- /dev/null +++ b/stream_fusion/utils/debrid/get_debrid_service.py @@ -0,0 +1,19 @@ +from fastapi.exceptions import HTTPException + +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.realdebrid import RealDebrid + + +def get_debrid_service(config): + service_name = config['service'] + if service_name == "realdebrid": + debrid_service = RealDebrid(config) + elif service_name == "alldebrid": + debrid_service = AllDebrid(config) + elif service_name == "premiumize": + debrid_service = Premiumize(config) + else: + raise HTTPException(status_code=500, detail="Invalid service configuration.") + + return debrid_service diff --git a/stream_fusion/utils/debrid/premiumize.py b/stream_fusion/utils/debrid/premiumize.py new file mode 100644 index 0000000..b84ff71 --- /dev/null +++ b/stream_fusion/utils/debrid/premiumize.py @@ -0,0 +1,120 @@ +# Assuming the BaseDebrid class and necessary imports are already defined as shown previously +import json + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import get_info_hash_from_magnet, season_episode_in_filename +from stream_fusion.logging_config import logger + + +class Premiumize(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://www.premiumize.me/api" + + def add_magnet(self, magnet, ip=None): + url = f"{self.base_url}/transfer/create?apikey={self.config['debridKey']}" + form = {'src': magnet} + return self.get_json_response(url, method='post', data=form) + + # Doesn't work for the time being. Premiumize does not support torrent file torrents + def add_torrent(self, torrent_file): + url = f"{self.base_url}/transfer/create?apikey={self.config['debridKey']}" + form = {'file': torrent_file} + return self.get_json_response(url, method='post', data=form) + + def list_transfers(self): + url = f"{self.base_url}/transfer/list?apikey={self.config['debridKey']}" + return self.get_json_response(url) + + def get_folder_or_file_details(self, item_id, is_folder=True): + if is_folder: + logger.info(f"Getting folder details with id: {item_id}") + url = f"{self.base_url}/folder/list?id={item_id}&apikey={self.config['debridKey']}" + else: + logger.info(f"Getting file details with id: {item_id}") + url = f"{self.base_url}/item/details?id={item_id}&apikey={self.config['debridKey']}" + return self.get_json_response(url) + + def get_availability(self, hash): + url = f"{self.base_url}/cache/check?apikey={self.config['debridKey']}&items[]={hash}" + return self.get_json_response(url) + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + url = f"{self.base_url}/cache/check?apikey={self.config['debridKey']}&items[]=" + "&items[]=".join( + hashes_or_magnets) + return self.get_json_response(url) + + def get_stream_link(self, query, config, ip=None): + query = json.loads(query) + magnet = query['magnet'] + logger.info(f"Received query for magnet: {magnet}") + info_hash = get_info_hash_from_magnet(magnet) + logger.info(f"Info hash extracted: {info_hash}") + stream_type = query['type'] + logger.info(f"Stream type: {stream_type}") + + transfer_data = self.add_magnet(magnet) + if not transfer_data or 'id' not in transfer_data: + logger.error("Failed to create transfer.") + return "Error: Failed to create transfer." + transfer_id = transfer_data['id'] + logger.info(f"Transfer created with ID: {transfer_id}") + + if not self.wait_for_ready_status(lambda: self.get_availability(info_hash)["transcoded"][0] is True): + logger.info("Torrent not ready, caching in progress") + return NO_CACHE_VIDEO_URL + + logger.info("Torrent is ready.") + + # Assuming the transfer is complete, we need to find whether it's a file or a folder + transfers = self.list_transfers() + item_id, is_folder = None, False + for item in transfers.get('transfers', []): + if item['id'] == transfer_id: + if item.get('folder_id'): + item_id = item['folder_id'] + is_folder = True + else: + item_id = item['file_id'] + break + + if not item_id: + logger.error("Transfer completed but no item ID found.") + return "Error: Transfer completed but no item ID found." + + details = self.get_folder_or_file_details(item_id, is_folder) + logger.info(f"Got details") + + if stream_type == "movie": + logger.info("Getting link for movie") + # For movies, we pick the largest file in the folder or the file itself + if is_folder: + link = max(details.get("content", []), key=lambda x: x["size"])["link"] + else: + link = details.get('link') + elif stream_type == "series": + logger.info("Getting link for series") + if is_folder: + season = query["season"] + episode = query["episode"] + files = details.get("content", []) + matching_files = [] + + for file in files: + if season_episode_in_filename(file["name"], season, episode): + matching_files.append(file) + + if len(matching_files) == 0: + logger.error(f"No matching files for {season} {episode} in torrent.") + return f"Error: No matching files for {season} {episode} in torrent." + + link = max(matching_files, key=lambda x: x["size"])["link"] + else: + link = details.get('link') + else: + logger.error("Unsupported stream type.") + return "Error: Unsupported stream type." + + logger.info(f"Link generated: {link}") + return link diff --git a/stream_fusion/utils/debrid/realdebrid.py b/stream_fusion/utils/debrid/realdebrid.py new file mode 100644 index 0000000..6df8bec --- /dev/null +++ b/stream_fusion/utils/debrid/realdebrid.py @@ -0,0 +1,285 @@ +import re +import json +import time +from urllib.parse import unquote + +import requests + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import get_info_hash_from_magnet +from stream_fusion.utils.general import is_video_file +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + + +class RealDebrid(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://api.real-debrid.com" + self.headers = {"Authorization": f"Bearer {self.config['debridKey']}"} + + def add_magnet(self, magnet, ip=None): + url = f"{self.base_url}/rest/1.0/torrents/addMagnet" + data = {"magnet": magnet} + logger.info(f"Adding magnet to RD: {magnet}") + return self.get_json_response(url, method='post', headers=self.headers, data=data) + + def add_torrent(self, torrent_file): + url = f"{self.base_url}/rest/1.0/torrents/addTorrent" + return self.get_json_response(url, method='put', headers=self.headers, data=torrent_file) + + def delete_torrent(self, id): + url = f"{self.base_url}/rest/1.0/torrents/delete/{id}" + return self.get_json_response(url, method='delete', headers=self.headers) + + def get_torrent_info(self, torrent_id): + logger.info(f"Getting torrent info for: {torrent_id}") + url = f"{self.base_url}/rest/1.0/torrents/info/{torrent_id}" + torrent_info = self.get_json_response(url, headers=self.headers) + + if not torrent_info or 'files' not in torrent_info: + return None + + return torrent_info + + def select_files(self, torrent_id, file_id): + logger.info(f"Selecting file(s): {file_id}") + url = f"{self.base_url}/rest/1.0/torrents/selectFiles/{torrent_id}" + data = {"files": str(file_id)} + requests.post(url, headers=self.headers, data=data) + + def unrestrict_link(self, link): + url = f"{self.base_url}/rest/1.0/unrestrict/link" + data = {"link": link} + return self.get_json_response(url, method='post', headers=self.headers, data=data) + + def is_already_added(self, magnet): + hash = magnet.split("urn:btih:")[1].split("&")[0].lower() + url = f"{self.base_url}/rest/1.0/torrents" + torrents = self.get_json_response(url, headers=self.headers) + for torrent in torrents: + if torrent['hash'].lower() == hash: + return torrent['id'] + return False + + def wait_for_link(self, torrent_id, timeout=30, interval=5): + start_time = time.time() + while time.time() - start_time < timeout: + torrent_info = self.get_torrent_info(torrent_id) + if torrent_info and 'links' in torrent_info and len(torrent_info['links']) > 0: + return torrent_info['links'] + time.sleep(interval) + + return None + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + if len(hashes_or_magnets) == 0: + logger.info("No hashes to be sent to Real-Debrid.") + return dict() + + url = f"{self.base_url}/rest/1.0/torrents/instantAvailability/{'/'.join(hashes_or_magnets)}" + return self.get_json_response(url, headers=self.headers) + + def get_stream_link(self, query_string, config, ip=None): + query = json.loads(query_string) + + magnet = query['magnet'] + stream_type = query['type'] + file_index = int(query['file_index']) if query['file_index'] is not None else None + season = query['season'] + episode = query['episode'] + torrent_download = unquote(query["torrent_download"]) if query["torrent_download"] is not None else None + info_hash = get_info_hash_from_magnet(magnet) + logger.info(f"RealDebrid get stream link for {stream_type} with hash: {info_hash}") + + cached_torrent_ids = self.__get_cached_torrent_ids(info_hash) + logger.info(f"Found {len(cached_torrent_ids)} cached torrents with hash: {info_hash}") + + torrent_info = None + if len(cached_torrent_ids) > 0: + if stream_type == "movie": + torrent_info = self.get_torrent_info(cached_torrent_ids[0]) + elif stream_type == "series": + torrent_info = self.__get_cached_torrent_info(cached_torrent_ids, file_index, season, episode) + else: + return "Error: Unsupported stream type." + + # The torrent is not yet added + if torrent_info is None: + torrent_info = self.__add_magnet_or_torrent(magnet, torrent_download, anon_magnet=config["anonymizeMagnets"]) + if not torrent_info or 'files' not in torrent_info: + return "Error: Failed to get torrent info." + + logger.info("Selecting file") + self.__select_file(torrent_info, stream_type, file_index, season, episode) + + # == operator, to avoid adding the season pack twice and setting 5 as season pack treshold + if len(cached_torrent_ids) == 0 and stream_type == "series" and len(torrent_info["files"]) > 5: + logger.info("Prefetching season pack") + prefetched_torrent_info = self.__prefetch_season_pack(magnet, torrent_download) + if len(prefetched_torrent_info["links"]) > 0: + self.delete_torrent(torrent_info["id"]) + torrent_info = prefetched_torrent_info + + torrent_id = torrent_info["id"] + logger.info(f"Waiting for the link(s) to be ready for torrent ID: {torrent_id}") + # Waiting for the link(s) to be ready + links = self.wait_for_link(torrent_id) + if links is None: + return NO_CACHE_VIDEO_URL + + if len(links) > 1: + logger.info("Finding appropiate link") + download_link = self.__find_appropiate_link(torrent_info, links, file_index, season, episode) + else: + download_link = links[0] + + logger.info(f"Unrestricting the download link: {download_link}") + # Unrestricting the download link + unrestrict_response = self.unrestrict_link(download_link) + if not unrestrict_response or 'download' not in unrestrict_response: + return "Error: Failed to unrestrict link." + + logger.info(f"Got download link: {unrestrict_response['download']}") + return unrestrict_response['download'] + + def __get_cached_torrent_ids(self, info_hash): + url = f"{self.base_url}/rest/1.0/torrents" + torrents = self.get_json_response(url, headers=self.headers) + + logger.info(f"Searching users real-debrid downloads for {info_hash}") + torrent_ids = [] + for torrent in torrents: + if torrent['hash'].lower() == info_hash: + torrent_ids.append(torrent['id']) + + return torrent_ids + + def __get_cached_torrent_info(self, cached_ids, file_index, season, episode): + cached_torrents = [] + for cached_torrent_id in cached_ids: + cached_torrent_info = self.get_torrent_info(cached_torrent_id) + if self.__torrent_contains_file(cached_torrent_info, file_index, season, episode): + if len(cached_torrent_info["links"]) > 0: # If the links are ready + return cached_torrent_info + + cached_torrents.append(cached_torrent_info) + + if len(cached_torrents) == 0: + return None + + return max(cached_torrents, key=lambda x: x['progress']) + + def __torrent_contains_file(self, torrent_info, file_index, season, episode): + if not torrent_info or "files" not in torrent_info: + return False + + if file_index is None: + for file in torrent_info["files"]: + if file["selected"] and season_episode_in_filename(file['path'], season, episode): + return True + else: + for file in torrent_info["files"]: + if file['id'] == file_index: + return file["selected"] == 1 + + return False + + def __add_magnet_or_torrent(self, magnet, torrent_download=None, anon_magnet=False): + torrent_id = "" + if torrent_download is None: + logger.info(f"Adding magnet to RealDebrid") + if anon_magnet: + magnet = re.sub(r'&tr=[^&]*', '', magnet) + magnet_response = self.add_magnet(magnet) + logger.info(f"RealDebrid add magnet response: {magnet_response}") + + if not magnet_response or 'id' not in magnet_response: + return "Error: Failed to add magnet." + + torrent_id = magnet_response['id'] + elif anon_magnet and torrent_download: + logger.info(f"Adding magnet to RealDebrid") + clean_magnet = re.sub(r'&tr=[^&]*', '', magnet) + magnet_response = self.add_magnet(clean_magnet) + logger.info(f"RealDebrid add magnet response: {magnet_response}") + + if not magnet_response or 'id' not in magnet_response: + return "Error: Failed to add magnet." + + torrent_id = magnet_response['id'] + else: + logger.info(f"Downloading torrent file from Jackett") + torrent_file = self.donwload_torrent_file(torrent_download) + logger.info(f"Torrent file downloaded from Jackett") + + logger.info(f"Adding torrent file to RealDebrid") + upload_response = self.add_torrent(torrent_file) + logger.info(f"RealDebrid add torrent file response: {upload_response}") + + if not upload_response or 'id' not in upload_response: + return "Error: Failed to add torrent file." + + torrent_id = upload_response['id'] + + logger.info(f"New torrent ID: {torrent_id}") + return self.get_torrent_info(torrent_id) + + def __prefetch_season_pack(self, magnet, torrent_download): + torrent_info = self.__add_magnet_or_torrent(magnet, torrent_download) + video_file_indexes = [] + + for file in torrent_info["files"]: + if is_video_file(file["path"]): + video_file_indexes.append(str(file["id"])) + + self.select_files(torrent_info["id"], ",".join(video_file_indexes)) + time.sleep(10) + return self.get_torrent_info(torrent_info["id"]) + + def __select_file(self, torrent_info, stream_type, file_index, season, episode): + torrent_id = torrent_info["id"] + if file_index is not None: + logger.info(f"Selecting file_index: {file_index}") + self.select_files(torrent_id, file_index) + return + + files = torrent_info["files"] + if stream_type == "movie": + largest_file_id = max(files, key=lambda x: x['bytes'])['id'] + logger.info(f"Selecting file_index: {largest_file_id}") + self.select_files(torrent_id, largest_file_id) + elif stream_type == "series": + matching_files = [] + for file in files: + if season_episode_in_filename(file["path"], season, episode): + matching_files.append(file) + + largest_file_id = max(matching_files, key=lambda x: x['bytes'])['id'] + logger.info(f"Selecting file_index: {largest_file_id}") + self.select_files(torrent_id, largest_file_id) + + def __find_appropiate_link(self, torrent_info, links, file_index, season, episode): + selected_files = list(filter(lambda file: file["selected"] == 1, torrent_info["files"])) + + index = 0 + if file_index is not None: + for file in selected_files: + if file["id"] == file_index: + break + index += 1 + else: + matching_indexes = [] + for file in selected_files: + if season_episode_in_filename(file["path"], season, episode): + matching_indexes.append({"index": index, "file": file}) + index += 1 + + index = max(matching_indexes, lambda x: x["file"]["bytes"])["index"] + + if len(links) - 1 < index: + logger.debug(f"From selected files {selected_files}, index: {index} is out of range for {links}.") + return NO_CACHE_VIDEO_URL + + return links[index] diff --git a/stream_fusion/utils/detection.py b/stream_fusion/utils/detection.py new file mode 100644 index 0000000..fb20271 --- /dev/null +++ b/stream_fusion/utils/detection.py @@ -0,0 +1,28 @@ +import re + + +def detect_languages(torrent_name): + language_patterns = { + "fr": r"\b(?:FR(?:ench|a|e|anc[eê]s)?|V(?:O?F(?:F|I|i)?|O?Q)|TRUEFRENCH|VOST(?:FR)?|SUBFRENCH)\b", + "en": r"\b(?:EN(?:G(?:LISH)?)?|VOST(?:EN)?|SUBBED)\b", + "es": r"\b(?:ES(?:P(?:ANISH)?)?|VOSE|SUBESP)\b", + "de": r"\b(?:DE(?:UTSCH|RMAN)?|GER(?:MAN)?|SUBGER)\b", + "it": r"\b(?:IT(?:A(?:LIAN)?)?|SUBITA)\b", + "pt": r"\b(?:PT(?:-BR)?|POR(?:TUGUESE)?|LEGENDADO)\b", + "ru": r"\b(?:RU(?:S(?:SIAN)?)?|SUBSRUS)\b", + "in": r"\b(?:INDIAN|HINDI|TELUGU|TAMIL|KANNADA|MALAYALAM|PUNJABI|MARATHI|BENGALI|GUJARATI|URDU|ODIA|ASSAMESE|KONKANI|MANIPURI|NEPALI|SANSKRIT|SINHALA|SINDHI|TIBETAN|BHOJPURI|DHIVEHI|KASHMIRI|KURUKH|MAITHILI|NEWARI|RAJASTHANI|SANTALI|SINDHI|TULU)\b", + "nl": r"\b(?:NL(?:D)?|DUTCH|SUBSNL)\b", + "hu": r"\b(?:HU(?:N(?:GARIAN)?)?|SUBHUN)\b", + "la": r"\b(?:LA(?:TIN(?:O)?)?)\b", + "multi": r"\b(?:MULTI(?:LANG(?:UE)?)?|DUAL(?:AUDIO)?|VF2)\b", + } + + languages = [] + for language, pattern in language_patterns.items(): + if re.search(pattern, torrent_name, re.IGNORECASE): + languages.append(language) + + if len(languages) == 0: + return ["en"] + + return languages diff --git a/stream_fusion/utils/filter/base_filter.py b/stream_fusion/utils/filter/base_filter.py new file mode 100644 index 0000000..148b31c --- /dev/null +++ b/stream_fusion/utils/filter/base_filter.py @@ -0,0 +1,15 @@ +class BaseFilter: + def __init__(self, config, additional_config=None): + self.config = config + self.item_type = additional_config + + def filter(self, data): + raise NotImplementedError + + def can_filter(self): + raise NotImplementedError + + def __call__(self, data): + if self.config is not None and self.can_filter(): + return self.filter(data) + return data diff --git a/stream_fusion/utils/filter/language_filter.py b/stream_fusion/utils/filter/language_filter.py new file mode 100644 index 0000000..a3b79e3 --- /dev/null +++ b/stream_fusion/utils/filter/language_filter.py @@ -0,0 +1,52 @@ +import re + +from stream_fusion.utils.filter.base_filter import BaseFilter + + +class LanguageFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.fr_regex_patterns = [ + r"^(BlackAngel|Choco|HDForever|MAX|ONLY|Psaro|Sicario|Tezcat74|TyrellCorp|Zapax)$", + r"^(BDHD|FtLi|Goldenyann|HeavyWeight|MARBLECAKE|MUSTANG|Obi|PEPiTE|QUEBEC63|QC63|ROMKENT)$", + r"^(FLOP|FRATERNiTY|FoX|Psaro)$", + r"^(DUSTiN|FCK|FrIeNdS|QUALiTY)$", + r"^(BDHD|FoX|FRATERNiTY|FrIeNdS|MAX|Psaro|T3KASHi)$", + r"^(FUJiSAN|HANAMi|HDForever|HeavyWeight|MARBLECAKE|MYSTERiON|NoNE|ONLY|ONLYMOViE|TkHD|UTT)$", + r"^(BONBON|FCK|FW|FoX|FRATERNiTY|FrIeNdS|MOONLY|MTDK|PATOPESTO|Psaro|T3KASHi|TFA)$", + r"^(ALLDAYiN|ARK01|FUJiSAN|HANAMi|HeavyWeight|NEO|NoNe|ONLYMOViE|Slay3R|TkHD)$", + r"^(4FR|AiR3D|AiRDOCS|AiRFORCE|AiRLiNE|AiRTV|AKLHD|AMB3R)$", + r"^(ANMWR|AVON|AYMO|AZR|BANKAi|BAWLS|BiPOLAR|BLACKPANTERS|BODIE|BOOLZ|BRiNK|CARAPiLS|CiELOS)$", + r"^(CiNEMA|CMBHD|CoRa|COUAC|CRYPT0|D4KiD|DEAL|DiEBEX|DUPLI|DUSS|ENJOi|EUBDS|FHD|FiDELiO|FiDO|ForceBleue)$", + r"^(FREAMON|FRENCHDEADPOOL2|FRiES|FUTiL|FWDHD|GHOULS|GiMBAP|GLiMMER|Goatlove|HERC|HiggsBoson|HiRoSHiMa)$", + r"^(HYBRiS|HyDe|JMT|JoKeR|JUSTICELEAGUE|KAZETV|L0SERNiGHT|LaoZi|LeON|LOFiDEL|LOST|LOWIMDB|LYPSG|MAGiCAL)$", + r"^(MANGACiTY|MAXAGAZ|MaxiBeNoul|McNULTY|MELBA|MiND|MORELAND|MUNSTER|MUxHD|NERDHD|NERO|NrZ|NTK|OBSTACLE)$", + r"^(OohLaLa|OOKAMI|PANZeR|PiNKPANTERS|PKPTRS|PRiDEHD|PROPJOE|PURE|PUREWASTEOFBW|ROUGH|RUDE|Ryotox|SAFETY)$", + r"^(SASHiMi|SEiGHT|SESKAPiLE|SHEEEiT|SHiNiGAMi(UHD)?|SiGeRiS|SILVIODANTE|SLEEPINGFOREST|SODAPOP|S4LVE|SPINE)$", + r"^(SPOiLER|STRINGERBELL|SUNRiSE|tFR|THENiGHTMAREiNHD|THiNK|THREESOME|TiMELiNE|TSuNaMi|UKDHD|UKDTV|ULSHD|Ulysse)$", + r"^(USUNSKiLLED|URY|VENUE|VFC|VoMiT|Wednesday29th|ZEST|ZiRCON)$", + ] + self.fr_regex = re.compile("|".join(self.fr_regex_patterns)) + + def filter(self, data): + filtered_data = [] + for torrent in data: + if not torrent.languages: + continue + + languages = torrent.languages.copy() + + if torrent.from_cache and "multi" in languages: + if self.fr_regex.search(torrent.raw_title): + languages.remove("multi") + + if "multi" in languages or any( + lang in self.config["languages"] for lang in languages + ): + torrent.languages = languages + filtered_data.append(torrent) + + return filtered_data + + def can_filter(self): + return self.config["languages"] is not None diff --git a/stream_fusion/utils/filter/max_size_filter.py b/stream_fusion/utils/filter/max_size_filter.py new file mode 100644 index 0000000..cf43f60 --- /dev/null +++ b/stream_fusion/utils/filter/max_size_filter.py @@ -0,0 +1,23 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class MaxSizeFilter(BaseFilter): + def __init__(self, config, additional_config=None): + super().__init__(config, additional_config) + self.max_size_bytes = int(self.config['maxSize']) * 1024 * 1024 * 1024 # Convertir Go en octets + + def filter(self, data): + filtered_data = [] + for torrent in data: + torrent_size = int(torrent.size) if isinstance(torrent.size, str) else torrent.size + if torrent_size <= self.max_size_bytes: + filtered_data.append(torrent) + else: + logger.debug(f"Excluded torrent due to size: {torrent.raw_title}, Size: {torrent_size / (1024*1024*1024):.2f} GB") + logger.info(f"MaxSizeFilter: input {len(data)}, output {len(filtered_data)}") + return filtered_data + + def can_filter(self): + return int(self.config['maxSize']) > 0 and self.item_type == 'movie' + diff --git a/stream_fusion/utils/filter/quality_exclusion_filter.py b/stream_fusion/utils/filter/quality_exclusion_filter.py new file mode 100644 index 0000000..3ad7ac7 --- /dev/null +++ b/stream_fusion/utils/filter/quality_exclusion_filter.py @@ -0,0 +1,43 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class QualityExclusionFilter(BaseFilter): + RIPS = {"HDRIP", "BRRIP", "BDRIP", "WEBRIP", "TVRIP", "VODRIP"} + CAMS = {"CAM", "TS", "TC", "R5", "DVDSCR", "HDTV", "PDTV", "DSR", "WORKPRINT", "VHSRIP", "HDCAM"} + + def __init__(self, config: dict): + super().__init__(config) + self.excluded_qualities = {quality.upper() for quality in self.config.get('exclusion', [])} + self.exclude_rips = "RIPS" in self.excluded_qualities + self.exclude_cams = "CAM" in self.excluded_qualities + + def filter(self, data): + return [ + stream for stream in data + if self._is_stream_allowed(stream) + ] + + def _is_stream_allowed(self, stream) -> bool: + if any(q.upper() in self.excluded_qualities for q in stream.parsed_data.quality): + logger.debug(f"Stream excluded due to main quality: {stream.parsed_data.quality}") + return False + + if stream.parsed_data.resolution: + for item in stream.parsed_data.resolution: + item_upper = item.upper() + if item_upper in self.excluded_qualities: + logger.debug(f"Stream excluded due to quality spec: {item}") + return False + if self.exclude_rips and item_upper in self.RIPS: + logger.debug(f"Stream excluded due to RIP: {item}") + return False + if self.exclude_cams and item_upper in self.CAMS: + logger.debug(f"Stream excluded due to CAM: {item}") + return False + + logger.debug("Stream allowed") + return True + + def can_filter(self) -> bool: + return bool(self.excluded_qualities) diff --git a/stream_fusion/utils/filter/results_per_quality_filter.py b/stream_fusion/utils/filter/results_per_quality_filter.py new file mode 100644 index 0000000..19f4205 --- /dev/null +++ b/stream_fusion/utils/filter/results_per_quality_filter.py @@ -0,0 +1,33 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class ResultsPerQualityFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.max_results_per_quality = int(self.config.get('resultsPerQuality', 5)) + + def filter(self, data): + filtered_items = [] + resolution_count = {} + + for item in data: + resolutions = getattr(item.parsed_data, 'resolution', []) + if not resolutions: + resolutions = ["?.BZH.?"] + + for resolution in resolutions: + if resolution not in resolution_count: + resolution_count[resolution] = 1 + filtered_items.append(item) + break + elif resolution_count[resolution] < self.max_results_per_quality: + resolution_count[resolution] += 1 + filtered_items.append(item) + break + + logger.info(f"ResultsPerQualityFilter: input {len(data)}, output {len(filtered_items)}") + return filtered_items + + def can_filter(self): + return self.max_results_per_quality > 0 diff --git a/stream_fusion/utils/filter/title_exclusion_filter.py b/stream_fusion/utils/filter/title_exclusion_filter.py new file mode 100644 index 0000000..c11c51d --- /dev/null +++ b/stream_fusion/utils/filter/title_exclusion_filter.py @@ -0,0 +1,32 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class TitleExclusionFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.excluded_keywords = {keyword.upper() for keyword in self.config.get('exclusionKeywords', [])} + + def filter(self, data): + filtered_items = [] + for stream in data: + if self._should_include_stream(stream): + filtered_items.append(stream) + + logger.info(f"TitleExclusionFilter: input {len(data)}, output {len(filtered_items)}") + return filtered_items + + def _should_include_stream(self, stream): + try: + title_upper = stream.raw_title.upper() + for keyword in self.excluded_keywords: + if keyword in title_upper: + logger.debug(f"Excluded stream: {stream.raw_title} (keyword: {keyword})") + return False + return True + except AttributeError: + logger.warning(f"Stream has no title attribute: {stream}") + return False + + def can_filter(self): + return bool(self.excluded_keywords) diff --git a/stream_fusion/utils/filter_results.py b/stream_fusion/utils/filter_results.py new file mode 100644 index 0000000..d266f2f --- /dev/null +++ b/stream_fusion/utils/filter_results.py @@ -0,0 +1,196 @@ +import re +from typing import List + +from RTN import title_match, RTN, DefaultRanking, SettingsModel, sort_torrents + +from stream_fusion.utils.filter.language_filter import LanguageFilter +from stream_fusion.utils.filter.max_size_filter import MaxSizeFilter +from stream_fusion.utils.filter.quality_exclusion_filter import QualityExclusionFilter +from stream_fusion.utils.filter.results_per_quality_filter import ResultsPerQualityFilter +from stream_fusion.utils.filter.title_exclusion_filter import TitleExclusionFilter +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger + +quality_order = {"4k": 0, "2160p": 0, "1080p": 1, "720p": 2, "480p": 3} + + +def sort_quality(item): + logger.debug(f"Evaluating quality for item: {item.raw_title}") + if len(item.parsed_data.data.resolution) == 0: + return float("inf"), True + resolution = item.parsed_data.data.resolution[0] + priority = quality_order.get(resolution, float("inf")) + return priority, item.parsed_data.data.resolution is None + + +def items_sort(items, config): + logger.info(f"Starting item sorting. Sort method: {config['sort']}") + settings = SettingsModel( + require=[], + exclude=config["exclusionKeywords"] + config["exclusion"], + preferred=[], + ) + + rtn = RTN(settings=settings, ranking_model=DefaultRanking()) + logger.debug("Applying RTN ranking to items") + torrents = [rtn.rank(item.raw_title, item.info_hash) for item in items] + sorted_torrents = sort_torrents(set(torrents)) + + for key, value in sorted_torrents.items(): + index = next((i for i, item in enumerate(items) if item.info_hash == key), None) + if index is not None: + items[index].parsed_data = value + + logger.info(f"Sorting items by method: {config['sort']}") + if config["sort"] == "quality": + sorted_items = sorted(items, key=sort_quality) + elif config["sort"] == "sizeasc": + sorted_items = sorted(items, key=lambda x: int(x.size)) + elif config["sort"] == "sizedesc": + sorted_items = sorted(items, key=lambda x: int(x.size), reverse=True) + elif config["sort"] == "qualitythensize": + sorted_items = sorted(items, key=lambda x: (sort_quality(x), -int(x.size))) + else: + logger.warning( + f"Unrecognized sort method: {config['sort']}. No sorting applied." + ) + sorted_items = items + + logger.info(f"Sorting complete. Number of sorted items: {len(sorted_items)}") + return sorted_items + +def filter_out_non_matching_movies(items, year): + logger.info(f"Filtering non-matching movies for year : {year}") + year_pattern = re.compile(rf'\b{year}\b') + filtered_items = [] + for item in items: + logger.debug(f"Checking item: {item.raw_title}") + if year_pattern.search(item.raw_title): + logger.debug("Match found") + filtered_items.append(item) + else: + logger.debug("No match found") + return filtered_items + +def filter_out_non_matching_series(items, season, episode): + logger.info( + f"Filtering non-matching items for season {season} and episode {episode}" + ) + filtered_items = [] + clean_season = season.replace("S", "") + clean_episode = episode.replace("E", "") + numeric_season = int(clean_season) + numeric_episode = int(clean_episode) + + for item in items: + logger.debug(f"Checking item: {item.raw_title}") + if len(item.parsed_data.season) == 0 and len(item.parsed_data.episode) == 0: + logger.debug("Item with no season and episode, skipped") + continue + if ( + len(item.parsed_data.episode) == 0 + and numeric_season in item.parsed_data.season + ): + logger.debug("Season match found, episode not specified") + filtered_items.append(item) + continue + if ( + numeric_season in item.parsed_data.season + and numeric_episode in item.parsed_data.episode + ): + logger.debug("Exact season and episode match found") + filtered_items.append(item) + continue + + logger.info( + f"Filtering complete. {len(filtered_items)} matching items found out of {len(items)} total" + ) + return filtered_items + + +def remove_non_matching_title(items, titles): + logger.info(f"Removing items not matching titles: {titles}") + filtered_items = [] + for item in items: + for title in titles: + if title_match(title, item.parsed_data.parsed_title): + filtered_items.append(item) + break + else: + logger.debug("No title match found, item skipped") + + logger.info( + f"Title filtering complete. {len(filtered_items)} items kept out of {len(items)} total" + ) + return filtered_items + + +def filter_items(items, media, config): + logger.info(f"Starting item filtering for media: {media.titles[0]}") + filters = { + "languages": LanguageFilter(config), + "maxSize": MaxSizeFilter(config, media.type), + "exclusionKeywords": TitleExclusionFilter(config), + "exclusion": QualityExclusionFilter(config), + "resultsPerQuality": ResultsPerQualityFilter(config), + } + + logger.info(f"Initial item count: {len(items)}") + + if media.type == "series": + logger.info(f"Filtering out non-matching series torrents") + items = filter_out_non_matching_series(items, media.season, media.episode) + logger.info(f"Item count after season/episode filtering: {len(items)}") + + if media.type == "movie": + logger.info(f"Filtering out non-matching movie torrents") + items = filter_out_non_matching_movies(items, media.year) + logger.info(f"Item count after year filtering: {len(items)}") + + items = remove_non_matching_title(items, media.titles) + logger.info(f"Item count after title filtering: {len(items)}") + + for filter_name, filter_instance in filters.items(): + try: + logger.info(f"Applying {filter_name} filter: {config[filter_name]}") + items = filter_instance(items) + logger.info(f"Item count after {filter_name} filter: {len(items)}") + except Exception as e: + logger.error(f"Error while applying {filter_name} filter", exc_info=e) + + logger.info(f"Filtering complete. Final item count: {len(items)}") + return items + + +def sort_items(items, config): + if config["sort"] is not None: + logger.info(f"Sorting items according to config: {config['sort']}") + return items_sort(items, config) + else: + logger.info("No sorting specified, returning items in original order") + return items + + +def merge_items( + cache_items: List[TorrentItem], search_items: List[TorrentItem] +) -> List[TorrentItem]: + logger.info( + f"Merging cached items ({len(cache_items)}) and search items ({len(search_items)})" + ) + merged_dict = {} + + def add_to_merged(item): + if item.raw_title not in merged_dict: + merged_dict[item.raw_title] = item + else: + if item.seeders > merged_dict[item.raw_title].seeders: + merged_dict[item.raw_title] = item + + for item in cache_items: + add_to_merged(item) + for item in search_items: + add_to_merged(item) + + merged_items = list(merged_dict.values()) + logger.info(f"Merging complete. Total unique items: {len(merged_items)}") + return merged_items diff --git a/stream_fusion/utils/general.py b/stream_fusion/utils/general.py new file mode 100644 index 0000000..55db098 --- /dev/null +++ b/stream_fusion/utils/general.py @@ -0,0 +1,42 @@ +from RTN import parse + +from stream_fusion.logging_config import logger + +video_formats = {".mkv", ".mp4", ".avi", ".mov", ".flv", ".wmv", ".webm", ".mpg", ".mpeg", ".m4v", ".3gp", ".3g2", + ".ogv", + ".ogg", ".drc", ".gif", ".gifv", ".mng", ".avi", ".mov", ".qt", ".wmv", ".yuv", ".rm", ".rmvb", ".asf", + ".amv", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".mpg", ".mpeg", ".m2v", ".m4v", + ".svi", ".3gp", ".3g2", ".mxf", ".roq", ".nsv", ".flv", ".f4v", ".f4p", ".f4a", ".f4b"} + + +def season_episode_in_filename(filename, season, episode): + if not is_video_file(filename): + return False + + parsed_name = parse(filename) + + return season in parsed_name.season and episode in parsed_name.episode + + +def get_info_hash_from_magnet(magnet: str): + exact_topic_index = magnet.find("xt=") + if exact_topic_index == -1: + logger.debug(f"No exact topic in magnet {magnet}") + return None + + exact_topic_substring = magnet[exact_topic_index:] + end_of_exact_topic = exact_topic_substring.find("&") + if end_of_exact_topic != -1: + exact_topic_substring = exact_topic_substring[:end_of_exact_topic] + + info_hash = exact_topic_substring[exact_topic_substring.rfind(":") + 1:] + + return info_hash.lower() + + +def is_video_file(filename): + extension_idx = filename.rfind(".") + if extension_idx == -1: + return False + + return filename[extension_idx:] in video_formats diff --git a/stream_fusion/utils/jackett/__init__.py b/stream_fusion/utils/jackett/__init__.py new file mode 100644 index 0000000..b47c95e --- /dev/null +++ b/stream_fusion/utils/jackett/__init__.py @@ -0,0 +1,6 @@ +from stream_fusion.utils.jackett.jackett_indexer import JackettIndexer +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.jackett.jackett_service import JackettService + + +__all__ = ["JackettIndexer", "JackettResult", "JackettService"] \ No newline at end of file diff --git a/stream_fusion/utils/jackett/jackett_indexer.py b/stream_fusion/utils/jackett/jackett_indexer.py new file mode 100644 index 0000000..12646ba --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_indexer.py @@ -0,0 +1,9 @@ +class JackettIndexer: + def __init__(self): + self.title = None + self.id = None + self.link = None + self.type = None + self.language = None + self.tv_search_capatabilities = None + self.movie_search_capatabilities = None diff --git a/stream_fusion/utils/jackett/jackett_result.py b/stream_fusion/utils/jackett/jackett_result.py new file mode 100644 index 0000000..7375232 --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_result.py @@ -0,0 +1,61 @@ +from RTN import parse + +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger + +class JackettResult: + def __init__(self): + self.raw_title = None # Raw title of the torrent + self.size = None # Size of the torrent + self.link = None # Download link for the torrent file or magnet url + self.indexer = None # Indexer + self.seeders = None # Seeders count + self.magnet = None # Magnet url + self.info_hash = None # infoHash by Jackett + self.privacy = None # public or private + self.from_cache = None + + # Extra processed details for further filtering + self.languages = None # Language of the torrent + self.type = None # series or movie + + self.parsed_data = None # Ranked result + + def convert_to_torrent_item(self): + return TorrentItem( + self.raw_title, + self.size, + self.magnet, + self.info_hash.lower() if self.info_hash is not None else None, + self.link, + self.seeders, + self.languages, + self.indexer, + self.privacy, + self.type, + self.parsed_data + ) + + def from_cached_item(self, cached_item, media): + if type(cached_item) is not dict: + logger.error(cached_item) + + self.info_hash = cached_item['hash'] + + if len(self.info_hash) != 40: + raise ValueError(f"The hash '{self.info_hash}' does not have the expected length of 40 characters.") + + parsed_result = parse(cached_item['title']) + + self.raw_title = cached_item['title'] + self.indexer = "Cache Public" # Cache doesn't return an indexer sadly (It stores it tho) + self.magnet = cached_item['magnet'] + self.link = cached_item['magnet'] + self.languages = cached_item['language'].split(";") if cached_item['language'] is not None else [] + self.seeders = cached_item['seeders'] + self.size = cached_item['size'] + self.type = media.type + self.from_cache = True + self.parsed_data = parsed_result + + return self diff --git a/stream_fusion/utils/jackett/jackett_service.py b/stream_fusion/utils/jackett/jackett_service.py new file mode 100644 index 0000000..c5b086c --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_service.py @@ -0,0 +1,279 @@ +import os +import queue +import threading +import time +import xml.etree.ElementTree as ET + +import requests +from RTN import parse, ParsedData + +from stream_fusion.utils.jackett.jackett_indexer import JackettIndexer +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.detection import detect_languages +from stream_fusion.logging_config import logger + + +class JackettService: + def __init__(self, config): + self.logger = logger + + self.__api_key = config['jackettApiKey'] + self.__base_url = f"{config['jackettHost']}/api/v2.0" + self.__session = requests.Session() + + def search(self, media): + self.logger.info("Started Jackett search for " + media.type + " " + media.titles[0]) + + indexers = self.__get_indexers() + threads = [] + results_queue = queue.Queue() # Create a Queue instance to hold the results + + # Define a wrapper function that calls the actual target function and stores its return value in the queue + def thread_target(media, indexer): + self.logger.info(f"Searching on {indexer.title}") + start_time = time.time() + + # Call the actual function + if isinstance(media, Movie): + result = self.__search_movie_indexer(media, indexer) + elif isinstance(media, Series): + result = self.__search_series_indexer(media, indexer) + else: + raise TypeError("Only Movie and Series is allowed as media!") + + self.logger.info( + f"Search on {indexer.title} took {time.time() - start_time} seconds and found {len([result for sublist in result for result in sublist])} results") + + results_queue.put(result) # Put the result in the queue + + for indexer in indexers: + # Pass the wrapper function as the target to Thread, with necessary arguments + threads.append(threading.Thread(target=thread_target, args=(media, indexer))) + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + results = [] + + # Retrieve results from the queue and append them to the results list + while not results_queue.empty(): + results.extend(results_queue.get()) + + flatten_results = [result for sublist in results for result in sublist] + + return self.__post_process_results(flatten_results, media) + + def __search_movie_indexer(self, movie, indexer): + + # url = f"{self.__base_url}/indexers/all/results/torznab/api?apikey={self.__api_key}&t=movie&cat=2000&q={movie.title}&year={movie.year}" + + has_imdb_search_capability = (os.getenv( + "DISABLE_JACKETT_IMDB_SEARCH") != "true" and indexer.movie_search_capatabilities is not None and 'imdbid' in indexer.movie_search_capatabilities) + + if has_imdb_search_capability: + languages = ['en'] + index_of_language = [index for index, lang in enumerate(movie.languages) if lang == 'en'][0] + titles = [movie.titles[index_of_language]] + elif indexer.language == "en": + languages = movie.languages + titles = movie.titles + else: + index_of_language = [index for index, lang in enumerate(movie.languages) if + lang == indexer.language or lang == 'en'] + languages = [movie.languages[index] for index in index_of_language] + titles = [movie.titles[index] for index in index_of_language] + + results = [] + + for index, lang in enumerate(languages): + params = { + 'apikey': self.__api_key, + 't': 'movie', + 'cat': '2000', + 'q': titles[index], + 'year': movie.year, + } + + if has_imdb_search_capability: + params['imdbid'] = movie.id + + url = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + url += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + try: + response = self.__session.get(url) + response.raise_for_status() + results.append(self.__get_torrent_links_from_xml(response.text)) + except Exception: + self.logger.exception( + f"An exception occured while searching for a movie on Jackett with indexer {indexer.title} and " + f"language {lang}.") + + return results + + def __search_series_indexer(self, series, indexer): + season = str(int(series.season.replace('S', ''))) + episode = str(int(series.episode.replace('E', ''))) + + has_imdb_search_capability = (os.getenv("DISABLE_JACKETT_IMDB_SEARCH") != "true" + and indexer.tv_search_capatabilities is not None + and 'imdbid' in indexer.tv_search_capatabilities) + if has_imdb_search_capability: + languages = ['en'] + index_of_language = [index for index, lang in enumerate(series.languages) if lang == 'en'][0] + titles = [series.titles[index_of_language]] + elif indexer.language == "en": + languages = series.languages + titles = series.titles + else: + index_of_language = [index for index, lang in enumerate(series.languages) if + lang == indexer.language or lang == 'en'] + languages = [series.languages[index] for index in index_of_language] + titles = [series.titles[index] for index in index_of_language] + + results = [] + + for index, lang in enumerate(languages): + params = { + 'apikey': self.__api_key, + 't': 'tvsearch', + 'cat': '5000', + 'q': titles[index], + } + + if has_imdb_search_capability: + params['imdbid'] = series.id + + url_title = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + url_title += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + url_season = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + params['season'] = season + url_season += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + url_ep = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + params['ep'] = episode + url_ep += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + try: + # Current functionality is that it returns if the season, episode search was successful. This is subject to change + # TODO: what should we prioritize? season, episode or title? + response_ep = self.__session.get(url_ep) + response_ep.raise_for_status() + + response_season = self.__session.get(url_season) + response_season.raise_for_status() + + data_ep = self.__get_torrent_links_from_xml(response_ep.text) + data_season = self.__get_torrent_links_from_xml(response_season.text) + + if data_ep: + results.append(data_ep) + if data_season: + results.append(data_season) + + if not data_ep and not data_season: + response_title = self.__session.get(url_title) + response_title.raise_for_status() + data_title = self.__get_torrent_links_from_xml(response_title.text) + if data_title: + results.append(data_title) + except Exception: + self.logger.exception( + f"An exception occured while searching for a series on Jackett with indexer {indexer.title} and language {lang}.") + + return results + + def __get_indexers(self): + url = f"{self.__base_url}/indexers/all/results/torznab/api?apikey={self.__api_key}&t=indexers&configured=true" + + try: + response = self.__session.get(url) + response.raise_for_status() + return self.__get_indexer_from_xml(response.text) + except Exception: + self.logger.exception("An exception occured while getting indexers from Jackett.") + return [] + + def __get_indexer_from_xml(self, xml_content): + xml_root = ET.fromstring(xml_content) + + indexer_list = [] + for item in xml_root.findall('.//indexer'): + indexer = JackettIndexer() + + indexer.title = item.find('title').text + indexer.id = item.attrib['id'] + indexer.link = item.find('link').text + indexer.type = item.find('type').text + indexer.language = item.find('language').text.split('-')[0] + + self.logger.info(f"Indexer: {indexer.title} - {indexer.link} - {indexer.type}") + + movie_search = item.find('.//searching/movie-search[@available="yes"]') + tv_search = item.find('.//searching/tv-search[@available="yes"]') + + if movie_search is not None: + indexer.movie_search_capatabilities = movie_search.attrib['supportedParams'].split(',') + else: + self.logger.info(f"Movie search not available for {indexer.title}") + + if tv_search is not None: + indexer.tv_search_capatabilities = tv_search.attrib['supportedParams'].split(',') + else: + self.logger.info(f"TV search not available for {indexer.title}") + + indexer_list.append(indexer) + + return indexer_list + + def __get_torrent_links_from_xml(self, xml_content): + xml_root = ET.fromstring(xml_content) + + result_list = [] + for item in xml_root.findall('.//item'): + result = JackettResult() + + result.seeders = item.find('.//torznab:attr[@name="seeders"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}).attrib['value'] + if int(result.seeders) <= 0: + continue + + result.raw_title = item.find('title').text + result.size = item.find('size').text + result.link = item.find('link').text + result.indexer = item.find('jackettindexer').text + result.privacy = item.find('type').text + + # TODO: I haven't seen this in the Jackett XML response. Is this still relevant? + # Or which indexers provide this? + magnet = item.find('.//torznab:attr[@name="magneturl"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}) + result.magnet = magnet.attrib['value'] if magnet is not None else None + + infoHash = item.find('.//torznab:attr[@name="infohash"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}) + result.info_hash = infoHash.attrib['value'] if infoHash is not None else None + + result_list.append(result) + + return result_list + + def __post_process_results(self, results, media): + for result in results: + parsed_result = parse(result.raw_title) + + result.parsed_data = parsed_result + result.languages = detect_languages(result.raw_title) + result.type = media.type + + if isinstance(media, Series): + result.season = media.season + result.episode = media.episode + + return results diff --git a/stream_fusion/utils/metdata/cinemeta.py b/stream_fusion/utils/metdata/cinemeta.py new file mode 100644 index 0000000..b8dd5f2 --- /dev/null +++ b/stream_fusion/utils/metdata/cinemeta.py @@ -0,0 +1,35 @@ +import requests + +from stream_fusion.utils.metdata.metadata_provider_base import MetadataProvider +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + + +class Cinemeta(MetadataProvider): + def get_metadata(self, id, type): + self.logger.info("Getting metadata for " + type + " with id " + id) + + full_id = id.split(":") + + url = f"https://v3-cinemeta.strem.io/meta/{type}/{full_id[0]}.json" + response = requests.get(url) + data = response.json() + + if type == "movie": + result = Movie( + id=id, + titles=[self.replace_weird_characters(data["meta"]["name"])], + year=data["meta"]["year"], + languages=["en"] + ) + else: + result = Series( + id=id, + titles=[self.replace_weird_characters(data["meta"]["name"])], + season="S{:02d}".format(int(full_id[1])), + episode="E{:02d}".format(int(full_id[2])), + languages=["en"] + ) + + self.logger.info("Got metadata for " + type + " with id " + id) + return result diff --git a/stream_fusion/utils/metdata/metadata_provider_base.py b/stream_fusion/utils/metdata/metadata_provider_base.py new file mode 100644 index 0000000..c43f93e --- /dev/null +++ b/stream_fusion/utils/metdata/metadata_provider_base.py @@ -0,0 +1,35 @@ +from stream_fusion.logging_config import logger + + +class MetadataProvider: + + def __init__(self, config): + self.config = config + self.logger = logger + + def replace_weird_characters(self, string): + corresp = { + 'ā': 'a', 'ă': 'a', 'ą': 'a', 'ć': 'c', 'č': 'c', 'ç': 'c', + 'ĉ': 'c', 'ċ': 'c', 'ď': 'd', 'đ': 'd', 'è': 'e', 'é': 'e', + 'ê': 'e', 'ë': 'e', 'ē': 'e', 'ĕ': 'e', 'ę': 'e', 'ě': 'e', + 'ĝ': 'g', 'ğ': 'g', 'ġ': 'g', 'ģ': 'g', 'ĥ': 'h', 'î': 'i', + 'ï': 'i', 'ì': 'i', 'í': 'i', 'ī': 'i', 'ĩ': 'i', 'ĭ': 'i', + 'ı': 'i', 'ĵ': 'j', 'ķ': 'k', 'ĺ': 'l', 'ļ': 'l', 'ł': 'l', + 'ń': 'n', 'ň': 'n', 'ñ': 'n', 'ņ': 'n', 'ʼn': 'n', 'ó': 'o', + 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ø': 'o', 'ō': 'o', 'ő': 'o', + 'œ': 'oe', 'ŕ': 'r', 'ř': 'r', 'ŗ': 'r', 'š': 's', 'ş': 's', + 'ś': 's', 'ș': 's', 'ß': 'ss', 'ť': 't', 'ţ': 't', 'ū': 'u', + 'ŭ': 'u', 'ũ': 'u', 'û': 'u', 'ü': 'u', 'ù': 'u', 'ú': 'u', + 'ų': 'u', 'ű': 'u', 'ŵ': 'w', 'ý': 'y', 'ÿ': 'y', 'ŷ': 'y', + 'ž': 'z', 'ż': 'z', 'ź': 'z', 'æ': 'ae', 'ǎ': 'a', 'ǧ': 'g', + 'ə': 'e', 'ƒ': 'f', 'ǐ': 'i', 'ǒ': 'o', 'ǔ': 'u', 'ǚ': 'u', + 'ǜ': 'u', 'ǹ': 'n', 'ǻ': 'a', 'ǽ': 'ae', 'ǿ': 'o', + } + + for weird_char in corresp: + string = string.replace(weird_char, corresp[weird_char]) + + return string + + def get_metadata(self, id, type): + raise NotImplementedError diff --git a/stream_fusion/utils/metdata/tmdb.py b/stream_fusion/utils/metdata/tmdb.py new file mode 100644 index 0000000..0aed92d --- /dev/null +++ b/stream_fusion/utils/metdata/tmdb.py @@ -0,0 +1,43 @@ +import requests + +from stream_fusion.utils.metdata.metadata_provider_base import MetadataProvider +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + +class TMDB(MetadataProvider): + def get_metadata(self, id, type): + self.logger.info("Getting metadata for " + type + " with id " + id) + + full_id = id.split(":") + + result = None + + for lang in self.config['languages']: + url = f"https://api.themoviedb.org/3/find/{full_id[0]}?api_key={self.config['tmdbApi']}&external_source=imdb_id&language={lang}" + response = requests.get(url) + data = response.json() + + if lang == self.config['languages'][0]: + if type == "movie": + result = Movie( + id=id, + titles=[self.replace_weird_characters(data["movie_results"][0]["title"])], + year=data["movie_results"][0]["release_date"][:4], + languages=self.config['languages'] + ) + else: + result = Series( + id=id, + titles=[self.replace_weird_characters(data["tv_results"][0]["name"])], + season="S{:02d}".format(int(full_id[1])), + episode="E{:02d}".format(int(full_id[2])), + languages=self.config['languages'] + ) + else: + if type == "movie": + result.titles.append(self.replace_weird_characters(data["movie_results"][0]["title"])) + else: + result.titles.append(self.replace_weird_characters(data["tv_results"][0]["name"])) + + self.logger.info("Got metadata for " + type + " with id " + id) + return result diff --git a/stream_fusion/utils/models/media.py b/stream_fusion/utils/models/media.py new file mode 100644 index 0000000..43398b3 --- /dev/null +++ b/stream_fusion/utils/models/media.py @@ -0,0 +1,6 @@ +class Media: + def __init__(self, id, titles, languages, type): + self.id = id + self.titles = titles + self.languages = languages + self.type = type diff --git a/stream_fusion/utils/models/movie.py b/stream_fusion/utils/models/movie.py new file mode 100644 index 0000000..121c458 --- /dev/null +++ b/stream_fusion/utils/models/movie.py @@ -0,0 +1,7 @@ +from stream_fusion.utils.models.media import Media + + +class Movie(Media): + def __init__(self, id, titles, year, languages): + super().__init__(id, titles, languages, "movie") + self.year = year diff --git a/stream_fusion/utils/models/series.py b/stream_fusion/utils/models/series.py new file mode 100644 index 0000000..c348de1 --- /dev/null +++ b/stream_fusion/utils/models/series.py @@ -0,0 +1,9 @@ +from stream_fusion.utils.models.media import Media + + +class Series(Media): + def __init__(self, id, titles, season, episode, languages): + super().__init__(id, titles, languages, "series") + self.season = season + self.episode = episode + self.seasonfile = None diff --git a/stream_fusion/utils/parse_config.py b/stream_fusion/utils/parse_config.py new file mode 100644 index 0000000..54389ef --- /dev/null +++ b/stream_fusion/utils/parse_config.py @@ -0,0 +1,16 @@ +import json + +from stream_fusion.utils.string_encoding import decodeb64 + + +def parse_config(b64config): + config = json.loads(decodeb64(b64config)) + + # For backwards compatibility + if "languages" not in config: + config["languages"] = [config["language"]] + + if "anonymizeMagnets" not in config: + config["anonymizeMagnets"] = False # Default to False if not specified + + return config diff --git a/stream_fusion/utils/security/__init__.py b/stream_fusion/utils/security/__init__.py new file mode 100644 index 0000000..931a7f7 --- /dev/null +++ b/stream_fusion/utils/security/__init__.py @@ -0,0 +1,5 @@ +"""Security utils.""" +from stream_fusion.utils.security.security_api_key import api_key_security +from stream_fusion.utils.security.security_secret import secret_based_security + +__all__ = ["secret_based_security", "api_key_security"] diff --git a/stream_fusion/utils/security/security_api_key.py b/stream_fusion/utils/security/security_api_key.py new file mode 100644 index 0000000..f225e3c --- /dev/null +++ b/stream_fusion/utils/security/security_api_key.py @@ -0,0 +1,43 @@ +"""Main dependency for other endpoints.""" +from fastapi import Security +from fastapi.security import APIKeyHeader, APIKeyQuery +from starlette.exceptions import HTTPException +from starlette.status import HTTP_403_FORBIDDEN + +from stream_fusion.services.security_db import security_db_access + +API_KEY_NAME = "api-key" + +api_key_query = APIKeyQuery( + name=API_KEY_NAME, + scheme_name="API key query", + auto_error=False, +) +api_key_header = APIKeyHeader( + name=API_KEY_NAME, + scheme_name="API key header", + auto_error=False, +) + + +async def api_key_security( + query_param: str = Security(api_key_query), + header_param: str = Security(api_key_header), +): + if not query_param and not header_param: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="An API key must be passed as query or header", + ) + + elif query_param and security_db_access.check_key(query_param): + return query_param + + elif header_param and security_db_access.check_key(header_param): + return header_param + + else: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="Wrong, revoked, or expired API key.", + ) diff --git a/stream_fusion/utils/security/security_secret.py b/stream_fusion/utils/security/security_secret.py new file mode 100644 index 0000000..956c688 --- /dev/null +++ b/stream_fusion/utils/security/security_secret.py @@ -0,0 +1,99 @@ +"""Secret dependency. +""" +import secrets +import uuid +from typing import Optional + +from fastapi import Security +from fastapi.security import APIKeyHeader +from starlette.exceptions import HTTPException +from starlette.status import HTTP_403_FORBIDDEN + +from stream_fusion.logging_config import logger +from stream_fusion.settings import settings + + +class GhostLoadedSecret: + """Ghost-loaded secret handler""" + + def __init__(self) -> None: + self._secret = None + + @property + def value(self): + if self._secret: + return self._secret + + else: + self._secret = self.get_secret_value() + return self.value + + def get_secret_value(self): + """Get secret value from environment variable or generate a new one.""" + if settings.secret_api_key: + try: + secret_value = settings.secret_api_key + except KeyError: + secret_value = str(uuid.uuid4()) + logger.warning( + ( + "ENVIRONMENT VARIABLE 'RD_SYNCRR_secret_api_key' FORMAT" + " INCORECT\n\tGenerated a single-use secret key for this" + f" session:\n\t{secret_value=}" + ), + ) + else: + secret_value = str(uuid.uuid4()) + logger.warning( + ( + "ENVIRONMENT VARIABLE 'RD_SYNCRR_secret_api_key' NOT FOUND\n" + "\tGenerated a single-use secret key for this session:\n" + f"\t{secret_value=}" + ), + ) + + return secret_value + + +secret = GhostLoadedSecret() + +SECRET_KEY_NAME = "secret-key" # noqa: S105 + +secret_header = APIKeyHeader( + name=SECRET_KEY_NAME, + scheme_name="Secret header", + auto_error=False, +) + + +async def secret_based_security(header_param: Optional[str] = Security(secret_header)): + """ + Args: + header_param: parsed header field secret_header + + Returns: + True if the authentication was successful + + Raises: + HTTPException if the authentication failed + """ + + if not header_param: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="secret_key must be passed as a header field", + ) + + # We simply return True if the given secret-key has the right value + if not secrets.compare_digest(header_param, secret.value): + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail=( + "Wrong secret key. If not set through environment variable" + " 'secret_api_key', it was generated automatically at" + " startup and appears in the server logs." + ), + ) + + else: + return True diff --git a/stream_fusion/utils/string_encoding.py b/stream_fusion/utils/string_encoding.py new file mode 100644 index 0000000..0f182fb --- /dev/null +++ b/stream_fusion/utils/string_encoding.py @@ -0,0 +1,9 @@ +import base64 + + +def encodeb64(data): + return base64.b64encode(data.encode('utf-8')).decode('utf-8') + + +def decodeb64(data): + return base64.b64decode(data).decode('utf-8') diff --git a/stream_fusion/utils/torrent/torrent_item.py b/stream_fusion/utils/torrent/torrent_item.py new file mode 100644 index 0000000..b1737d5 --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_item.py @@ -0,0 +1,93 @@ +from RTN import parse +from urllib.parse import quote + +from stream_fusion.utils.models.media import Media +from stream_fusion.utils.models.series import Series +from stream_fusion.logging_config import logger + + +class TorrentItem: + def __init__(self, raw_title, size, magnet, info_hash, link, seeders, languages, indexer, + privacy, type=None, parsed_data=None): + self.logger = logger + + self.raw_title = raw_title # Raw title of the torrent + self.size = size # Size of the video file inside the torrent - it may be updated during __process_torrent() + self.magnet = magnet # Magnet to torrent + self.info_hash = info_hash # Hash of the torrent + self.link = link # Link to download torrent file or magnet link + self.seeders = seeders # The number of seeders + self.languages = languages # Language of the torrent + self.indexer = indexer # Indexer of the torrent + self.type = type # "series" or "movie" + self.privacy = privacy # "public" or "private" + + self.file_name = None # it may be updated during __process_torrent() + self.files = None # The files inside of the torrent. If it's None, it means that there is only one file inside of the torrent + self.torrent_download = None # The torrent jackett download url if its None, it means that there is only a magnet link provided by Jackett. It also means, that we cant do series file filtering before debrid. + self.trackers = [] # Trackers of the torrent + self.file_index = None # Index of the file inside of the torrent - it may be updated durring __process_torrent() and update_availability(). If the index is None and torrent is not None, it means that the series episode is not inside of the torrent. + + self.availability = False # If it's instantly available on the debrid service + + self.parsed_data = parsed_data # Ranked result + + def to_debrid_stream_query(self, media: Media) -> dict: + return { + "magnet": self.magnet, + "type": self.type, + "file_index": self.file_index, + "season": media.season if isinstance(media, Series) else None, + "episode": media.episode if isinstance(media, Series) else None, + "torrent_download": quote(self.torrent_download) if self.torrent_download is not None else None + } + + def to_dict(self): + return { + 'raw_title': self.raw_title, + 'size': self.size, + 'magnet': self.magnet, + 'info_hash': self.info_hash, + 'link': self.link, + 'seeders': self.seeders, + 'languages': self.languages, + 'indexer': self.indexer, + 'type': self.type, + 'privacy': self.privacy, + 'file_name': self.file_name, + 'files': self.files, + 'torrent_download': self.torrent_download, + 'trackers': self.trackers, + 'file_index': self.file_index, + 'availability': self.availability, + } + + @classmethod + def from_dict(cls, data): + if not isinstance(data, dict): + logger.error(f"Expected dict, got {type(data)}") + return None + + instance = cls( + raw_title=data['raw_title'], + size=data['size'], + magnet=data['magnet'], + info_hash=data['info_hash'], + link=data['link'], + seeders=data['seeders'], + languages=data['languages'], + indexer=data['indexer'], + privacy=data['privacy'], + type=data['type'] + ) + + instance.file_name = data['file_name'] + instance.files = data['files'] + instance.torrent_download = data['torrent_download'] + instance.trackers = data['trackers'] + instance.file_index = data['file_index'] + instance.availability = data['availability'] + + instance.parsed_data = parse(instance.raw_title) + + return instance diff --git a/stream_fusion/utils/torrent/torrent_service.py b/stream_fusion/utils/torrent/torrent_service.py new file mode 100644 index 0000000..9017f1c --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_service.py @@ -0,0 +1,195 @@ +import hashlib +import queue +import threading +import time +import urllib.parse +from typing import List + +import bencode +import requests +from RTN import parse + +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.zilean.zilean_result import ZileanResult +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.general import get_info_hash_from_magnet +from stream_fusion.logging_config import logger + +class TorrentService: + def __init__(self): + self.logger = logger + self.__session = requests.Session() + + def convert_and_process(self, results: List[JackettResult | ZileanResult]): + threads = [] + torrent_items_queue = queue.Queue() + + def thread_target(result: JackettResult | ZileanResult): + torrent_item = result.convert_to_torrent_item() + + if torrent_item.link.startswith("magnet:"): + processed_torrent_item = self.__process_magnet(torrent_item) + else: + processed_torrent_item = self.__process_web_url(torrent_item) + + torrent_items_queue.put(processed_torrent_item) + + for result in results: + threads.append(threading.Thread(target=thread_target, args=(result,))) + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + torrent_items_result = [] + + while not torrent_items_queue.empty(): + torrent_items_result.append(torrent_items_queue.get()) + + return torrent_items_result + + def __process_web_url(self, result: TorrentItem): + try: + # TODO: is the timeout enough? + time.sleep(0.2) + response = self.__session.get(result.link, allow_redirects=False, timeout=40) + except requests.exceptions.RequestException: + self.logger.error(f"Error while processing url: {result.link}") + return result + except requests.exceptions.ReadTimeout: + self.logger.error(f"Timeout while processing url: {result.link}") + return result + + if response.status_code == 200: + return self.__process_torrent(result, response.content) + elif response.status_code == 302: + result.magnet = response.headers['Location'] + return self.__process_magnet(result) + else: + self.logger.error(f"Error code {response.status_code} while processing url: {result.link}") + + return result + + def __process_torrent(self, result: TorrentItem, torrent_file): + metadata = bencode.bdecode(torrent_file) + + result.torrent_download = result.link + result.trackers = self.__get_trackers_from_torrent(metadata) + result.info_hash = self.__convert_torrent_to_hash(metadata["info"]) + result.magnet = self.__build_magnet(result.info_hash, metadata["info"]["name"], result.trackers) + + if "files" not in metadata["info"]: + result.file_index = 1 + return result + + result.files = metadata["info"]["files"] + + if result.type == "series": + file_details = self.__find_episode_file(result.files, result.parsed_data.season, result.parsed_data.episode) + + if file_details is not None: + self.logger.info("File details") + self.logger.info(file_details) + result.file_index = file_details["file_index"] + result.file_name = file_details["title"] + result.size = file_details["size"] + else: + result.file_index = self.__find_movie_file(result.files) + + return result + + def __process_magnet(self, result: TorrentItem): + if result.magnet is None: + result.magnet = result.link + + if result.info_hash is None: + result.info_hash = get_info_hash_from_magnet(result.magnet) + + result.trackers = self.__get_trackers_from_magnet(result.magnet) + + return result + + def __convert_torrent_to_hash(self, torrent_contents): + hashcontents = bencode.bencode(torrent_contents) + hexHash = hashlib.sha1(hashcontents).hexdigest() + return hexHash.lower() + + def __build_magnet(self, hash, display_name, trackers): + magnet_base = "magnet:?xt=urn:btih:" + magnet = f"{magnet_base}{hash}&dn={display_name}" + + if len(trackers) > 0: + magnet = f"{magnet}&tr={'&tr='.join(trackers)}" + + return magnet + + def __get_trackers_from_torrent(self, torrent_metadata): + # Sometimes list, sometimes string + announce = torrent_metadata["announce"] if "announce" in torrent_metadata else [] + # Sometimes 2D array, sometimes 1D array + announce_list = torrent_metadata["announce-list"] if "announce-list" in torrent_metadata else [] + + trackers = set() + if isinstance(announce, str): + trackers.add(announce) + elif isinstance(announce, list): + for tracker in announce: + trackers.add(tracker) + + for announce_list_item in announce_list: + if isinstance(announce_list_item, list): + for tracker in announce_list_item: + trackers.add(tracker) + if isinstance(announce_list_item, str): + trackers.add(announce_list_item) + + return list(trackers) + + def __get_trackers_from_magnet(self, magnet: str): + url_parts = urllib.parse.urlparse(magnet) + query_parts = urllib.parse.parse_qs(url_parts.query) + + trackers = [] + if "tr" in query_parts: + trackers = query_parts["tr"] + + return trackers + + def __find_episode_file(self, file_structure, season, episode): + + if len(season) == 0 or len(episode) == 0: + return None + + file_index = 1 + strict_episode_files = [] + episode_files = [] + for files in file_structure: + for file in files["path"]: + + parsed_file = parse(file) + + if season[0] in parsed_file.season and episode[0] in parsed_file.episode: + episode_files.append({ + "file_index": file_index, + "title": file, + "size": files["length"] + }) + + # Doesn't that need to be indented? + file_index += 1 + + return max(episode_files, key=lambda file: file["size"]) + + def __find_movie_file(self, file_structure): + max_size = 0 + max_file_index = 1 + current_file_index = 1 + for files in file_structure: + if files["length"] > max_size: + max_file_index = current_file_index + max_size = files["length"] + current_file_index += 1 + + return max_file_index diff --git a/stream_fusion/utils/torrent/torrent_smart_container.py b/stream_fusion/utils/torrent/torrent_smart_container.py new file mode 100644 index 0000000..b150d79 --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_smart_container.py @@ -0,0 +1,212 @@ +import threading + +from typing import List, Dict +from RTN import parse + +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.realdebrid import RealDebrid +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.cache.cache import cache_public +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + +class TorrentSmartContainer: + def __init__(self, torrent_items: List[TorrentItem], media): + self.logger = logger + self.logger.info(f"Initializing TorrentSmartContainer with {len(torrent_items)} items") + self.__itemsDict: Dict[TorrentItem] = self.__build_items_dict_by_infohash(torrent_items) + self.__media = media + + def get_hashes(self): + hashes = list(self.__itemsDict.keys()) + self.logger.debug(f"Retrieved {len(hashes)} hashes") + return hashes + + def get_items(self): + items = list(self.__itemsDict.values()) + self.logger.debug(f"Retrieved {len(items)} items") + return items + + def get_direct_torrentable(self): + self.logger.info("Retrieving direct torrentable items") + direct_torrentable_items = [] + for torrent_item in self.__itemsDict.values(): + if torrent_item.privacy == "public" and torrent_item.file_index is not None: + direct_torrentable_items.append(torrent_item) + self.logger.info(f"Found {len(direct_torrentable_items)} direct torrentable items") + return direct_torrentable_items + + def get_best_matching(self): + self.logger.info("Finding best matching items") + best_matching = [] + self.logger.debug(f"Total items to process: {len(self.__itemsDict)}") + for torrent_item in self.__itemsDict.values(): + self.logger.debug(f"Processing item: {torrent_item.raw_title} - Has torrent: {torrent_item.torrent_download is not None}") + if torrent_item.torrent_download is not None: + self.logger.debug(f"Has file index: {torrent_item.file_index is not None}") + if torrent_item.file_index is not None: + best_matching.append(torrent_item) + self.logger.debug("Item added to best matching (has file index)") + else: + best_matching.append(torrent_item) + self.logger.debug("Item added to best matching (magnet link)") + self.logger.info(f"Found {len(best_matching)} best matching items") + return best_matching + + def cache_container_items(self): + self.logger.info("Starting cache process for container items") + threading.Thread(target=self.__save_to_cache).start() + + def __save_to_cache(self): + self.logger.info("Saving public items to cache") + public_torrents = list(filter(lambda x: x.privacy == "public", self.get_items())) + self.logger.debug(f"Found {len(public_torrents)} public torrents to cache") + cache_public(public_torrents, self.__media) + self.logger.info("Caching process completed") + + def update_availability(self, debrid_response, debrid_type, media): + self.logger.info(f"Updating availability for {debrid_type.__name__}") + if debrid_type is RealDebrid: + self.__update_availability_realdebrid(debrid_response, media) + elif debrid_type is AllDebrid: + self.__update_availability_alldebrid(debrid_response, media) + elif debrid_type is Premiumize: + self.__update_availability_premiumize(debrid_response) + else: + self.logger.error(f"Unsupported debrid type: {debrid_type.__name__}") + raise NotImplementedError(f"Debrid type {debrid_type.__name__} not implemented") + + def __update_availability_realdebrid(self, response, media): + self.logger.info("Updating availability for RealDebrid") + for info_hash, details in response.items(): + if "rd" not in details: + self.logger.debug(f"Skipping hash {info_hash}: no RealDebrid data") + continue + torrent_item: TorrentItem = self.__itemsDict[info_hash] + self.logger.debug(f"Processing {torrent_item.type}: {torrent_item.raw_title}") + files = [] + if torrent_item.type == "series": + self.__process_series_files(details, media, torrent_item, files) + else: + self.__process_movie_files(details, files) + self.__update_file_details(torrent_item, files) + self.logger.info("RealDebrid availability update completed") + + def __process_series_files(self, details, media, torrent_item, files): + for variants in details["rd"]: + file_found = False + for file_index, file in variants.items(): + clean_season = media.season.replace("S", "") + clean_episode = media.episode.replace("E", "") + numeric_season = int(clean_season) + numeric_episode = int(clean_episode) + if season_episode_in_filename(file["filename"], numeric_season, numeric_episode): + self.logger.debug(f"Matching file found: {file['filename']}") + torrent_item.file_index = file_index + torrent_item.file_name = file["filename"] + torrent_item.size = file["filesize"] + torrent_item.availability = True + file_found = True + files.append({ + "file_index": file_index, + "title": file["filename"], + "size": file["filesize"] + }) + break + if file_found: + break + + def __process_movie_files(self, details, files): + for variants in details["rd"]: + for file_index, file in variants.items(): + self.logger.debug(f"Adding movie file: {file['filename']}") + files.append({ + "file_index": file_index, + "title": file["filename"], + "size": file["filesize"] + }) + + def __update_availability_alldebrid(self, response, media): + self.logger.info("Updating availability for AllDebrid") + if response["status"] != "success": + self.logger.error(f"AllDebrid API error: {response}") + return + for data in response["data"]["magnets"]: + if not data["instant"]: + self.logger.debug(f"Skipping non-instant magnet: {data['hash']}") + continue + torrent_item: TorrentItem = self.__itemsDict[data["hash"]] + files = [] + self.__explore_folders(data["files"], files, 1, torrent_item.type, media.season, media.episode) + self.__update_file_details(torrent_item, files) + self.logger.info("AllDebrid availability update completed") + + def __update_availability_premiumize(self, response): + self.logger.info("Updating availability for Premiumize") + if response["status"] != "success": + self.logger.error(f"Premiumize API error: {response}") + return + torrent_items = self.get_items() + for i, is_available in enumerate(response["response"]): + if bool(is_available): + torrent_items[i].availability = response["transcoded"][i] + self.logger.debug(f"Updated availability for item {i}: {torrent_items[i].availability}") + self.logger.info("Premiumize availability update completed") + + def __update_file_details(self, torrent_item, files): + if not files: + self.logger.debug(f"No files to update for {torrent_item.raw_title}") + return + file = max(files, key=lambda file: file["size"]) + torrent_item.availability = True + torrent_item.file_index = file["file_index"] + torrent_item.file_name = file["title"] + torrent_item.size = file["size"] + self.logger.debug(f"Updated file details for {torrent_item.raw_title}: {file['title']}") + + def __build_items_dict_by_infohash(self, items: List[TorrentItem]): + self.logger.info(f"Building items dictionary by infohash ({len(items)} items)") + items_dict = {} + for item in items: + if item.info_hash is not None: + if item.info_hash not in items_dict: + self.logger.debug(f"Adding {item.info_hash} to items dict") + items_dict[item.info_hash] = item + else: + self.logger.debug(f"Skipping duplicate info hash: {item.info_hash}") + self.logger.info(f"Built dictionary with {len(items_dict)} unique items") + return items_dict + + def __explore_folders(self, folder, files, file_index, type, season=None, episode=None): + if episode is None or season is None: + return file_index + + if type == "series": + for file in folder: + if "e" in file: + file_index = self.__explore_folders(file["e"], files, file_index, type, season, episode) + continue + parsed_file = parse(file["n"]) + if season in parsed_file.season and episode in parsed_file.episode: + self.logger.debug(f"Matching series file found: {file['n']}") + files.append({ + "file_index": file_index, + "title": file["n"], + "size": file["s"] if "s" in file else 0 + }) + file_index += 1 + elif type == "movie": + file_index = 1 + for file in folder: + if "e" in file: + file_index = self.__explore_folders(file["e"], files, file_index, type) + continue + self.logger.debug(f"Adding movie file: {file['n']}") + files.append({ + "file_index": file_index, + "title": file["n"], + "size": file["s"] if "s" in file else 0 + }) + file_index += 1 + return file_index \ No newline at end of file diff --git a/stream_fusion/utils/zilean/zilean_result.py b/stream_fusion/utils/zilean/zilean_result.py new file mode 100644 index 0000000..14da751 --- /dev/null +++ b/stream_fusion/utils/zilean/zilean_result.py @@ -0,0 +1,63 @@ +from RTN import parse + +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger +from stream_fusion.utils.detection import detect_languages + + +class ZileanResult: + def __init__(self): + self.raw_title = None # Raw title of the torrent + self.size = None # Size of the torrent + self.link = None # Download link for the torrent file or magnet url + self.indexer = None # Indexer + self.seeders = None # Seeders count + self.magnet = None # Magnet url + self.info_hash = None # infoHash by Jackett + self.privacy = None # public or private + self.from_cache = None + + # Extra processed details for further filtering + self.languages = None # Language of the torrent + self.type = None # series or movie + + self.parsed_data = None # Ranked result + + def convert_to_torrent_item(self): + return TorrentItem( + self.raw_title, + self.size, + self.magnet, + self.info_hash.lower() if self.info_hash is not None else None, + self.link, + self.seeders, + self.languages, + self.indexer, + self.privacy, + self.type, + self.parsed_data + ) + + def from_api_cached_item(self, api_cached_item, media): + if type(api_cached_item) is not dict: + logger.error(api_cached_item) + + self.info_hash = api_cached_item['infoHash'] + if len(self.info_hash) != 40: + raise ValueError(f"The hash '{self.info_hash}' does not have the expected length of 40 characters.") + + parsed_result = parse(api_cached_item['filename']) + + self.raw_title = parsed_result.raw_title + self.indexer = "DMM API" + self.magnet = "magnet:?xt=urn:btih:" + self.info_hash + self.link = self.magnet + self.languages = detect_languages(self.raw_title) + self.seeders = 0 + self.size = api_cached_item['filesize'] + self.type = media.type + self.privacy = "private" + self.from_cache = True + self.parsed_data = parsed_result + + return self diff --git a/stream_fusion/utils/zilean/zilean_service.py b/stream_fusion/utils/zilean/zilean_service.py new file mode 100644 index 0000000..a65ab7e --- /dev/null +++ b/stream_fusion/utils/zilean/zilean_service.py @@ -0,0 +1,99 @@ +import re +import requests + +from concurrent.futures import ThreadPoolExecutor, as_completed + +from stream_fusion.logging_config import logger +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + + +class ZileanService: + def __init__(self, config): + self.base_url = config["zileanUrl"] + self.search_endpoint = "/dmm/search" + self.session = requests.Session() + self.logger = logger + self.max_workers = config.get("max_workers", 4) + + def search(self, media): + if isinstance(media, Movie): + return self.__search_movie(media) + elif isinstance(media, Series): + return self.__search_series(media) + else: + raise TypeError("Only Movie and Series are allowed as media!") + + # TODO: remove duplicate title from titles list + def __clean_title(self, title): + pronouns_to_remove = [ + 'le', 'la', 'les', 'l\'', 'un', 'une', 'des', 'du', 'de', 'à', 'au', 'aux', + 'the', 'a', 'an', 'some', 'of', 'to', 'at', 'in', 'on', 'for', + 'he', 'she', 'it', 'they', 'we', 'you', 'i', 'me', 'him', 'her', 'them', 'us', + 'il', 'elle', 'on', 'nous', 'vous', 'ils', 'elles', 'je', 'tu', 'moi', 'toi', 'lui' + ] + title = title.lower() + title = re.sub(r'[^a-zA-Z0-9\s]', ' ', title) + words = title.split() + words = [word for word in words if word not in pronouns_to_remove] + cleaned_title = ' '.join(words) + cleaned_title = re.sub(r'\s+', ' ', cleaned_title).strip() + return cleaned_title + + def __deduplicate_api_results(self, api_results): + unique_results = set() + deduplicated_results = [] + + for result in api_results: + result_tuple = tuple(sorted(result.items())) + + if result_tuple not in unique_results: + unique_results.add(result_tuple) + deduplicated_results.append(result) + + return deduplicated_results + + def __remove_duplicate_titles(self, titles): + seen = set() + return [title for title in titles if not (title.lower() in seen or seen.add(title.lower()))] + + def __search_movie(self, movie): + unique_titles = self.__remove_duplicate_titles(movie.titles) + clean_titles = [self.__clean_title(title) for title in unique_titles] + return self.__threaded_search(clean_titles) + + def __search_series(self, series): + unique_titles = self.__remove_duplicate_titles(series.titles) + clean_titles = [self.__clean_title(title) for title in unique_titles] + search_texts = clean_titles.copy() + + if hasattr(series, 'season') and hasattr(series, 'episode'): + search_texts.extend([f"{title} {series.season}{series.episode}" for title in clean_titles]) + + return self.__threaded_search(search_texts) + + def __threaded_search(self, search_texts): + results = [] + with ThreadPoolExecutor(max_workers=self.max_workers) as executor: + future_to_text = {executor.submit(self.__make_request, text): text for text in search_texts} + for future in as_completed(future_to_text): + results.extend(future.result()) + return self.__deduplicate_api_results(results) + + def __make_request(self, query_text): + payload = {"queryText": query_text} + headers = {"Content-Type": "application/json"} + url = self.base_url + self.search_endpoint + + try: + response = self.session.post(url, json=payload, headers=headers, timeout=10) + response.raise_for_status() + json_response = response.json() + if isinstance(json_response, list): + return json_response + else: + self.logger.warning(f"Unexpected response format for query: {query_text}") + return [] + except Exception as e: + self.logger.exception(f"An exception occurred while searching for '{query_text}' on Zilean: {str(e)}") + return [] \ No newline at end of file diff --git a/stream_fusion/version.py b/stream_fusion/version.py new file mode 100644 index 0000000..1e086b7 --- /dev/null +++ b/stream_fusion/version.py @@ -0,0 +1,12 @@ +import toml +from stream_fusion.settings import settings + + +def get_version(): + with open(settings.version_path, "r") as f: + pyproject_data = toml.load(f) + return pyproject_data["tool"]["poetry"]["version"] + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/stream_fusion/videos/nocache.mp4 b/stream_fusion/videos/nocache.mp4 new file mode 100644 index 0000000..0e24b78 Binary files /dev/null and b/stream_fusion/videos/nocache.mp4 differ diff --git a/stream_fusion/web/__init__.py b/stream_fusion/web/__init__.py new file mode 100644 index 0000000..634aa90 --- /dev/null +++ b/stream_fusion/web/__init__.py @@ -0,0 +1 @@ +"""WEB API for stream_fusion.""" diff --git a/stream_fusion/web/api/__init__.py b/stream_fusion/web/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stream_fusion/web/api/auth/__init__.py b/stream_fusion/web/api/auth/__init__.py new file mode 100644 index 0000000..c12befc --- /dev/null +++ b/stream_fusion/web/api/auth/__init__.py @@ -0,0 +1,4 @@ +"""Auth by apikey endpoint.""" +from stream_fusion.web.api.auth.views import router + +__all__ = ["router"] diff --git a/stream_fusion/web/api/auth/schemas.py b/stream_fusion/web/api/auth/schemas.py new file mode 100644 index 0000000..ffd7262 --- /dev/null +++ b/stream_fusion/web/api/auth/schemas.py @@ -0,0 +1,17 @@ +from typing import Optional + +from pydantic import BaseModel + + +class UsageLog(BaseModel): + api_key: str + name: Optional[str] + is_active: bool + never_expire: bool + expiration_date: str + latest_query_date: Optional[str] + total_queries: int + + +class UsageLogs(BaseModel): + logs: list[UsageLog] diff --git a/stream_fusion/web/api/auth/views.py b/stream_fusion/web/api/auth/views.py new file mode 100644 index 0000000..132dc95 --- /dev/null +++ b/stream_fusion/web/api/auth/views.py @@ -0,0 +1,92 @@ +from fastapi import APIRouter, Depends, Query + +from stream_fusion.services.security_db import security_db_access +from stream_fusion.settings import settings +from stream_fusion.utils.security import secret_based_security +from stream_fusion.web.api.auth.schemas import UsageLog, UsageLogs + +router = APIRouter() + + +@router.get( + "/new", + dependencies=[Depends(secret_based_security)], + include_in_schema=settings.security_hide_docs, +) +def get_new_api_key( + name: str = Query( + None, + description="set API key name", + ), + never_expires: bool = Query( + False, + description="if set, the created API key will never be considered expired", + ), +) -> str: + """ + Returns: + api_key: a newly generated API key + """ + return security_db_access.create_key(name, never_expires) + + +@router.get( + "/revoke", + dependencies=[Depends(secret_based_security)], + include_in_schema=settings.security_hide_docs, +) +def revoke_api_key( + api_key: str = Query(..., alias="api-key", description="the api_key to revoke"), +): + """ + Revokes the usage of the given API key + + """ + return security_db_access.revoke_key(api_key) + + +@router.get( + "/renew", + dependencies=[Depends(secret_based_security)], + include_in_schema=settings.security_hide_docs, +) +def renew_api_key( + api_key: str = Query(..., alias="api-key", description="the API key to renew"), + expiration_date: str = Query( + None, + alias="expiration-date", + description="the new expiration date in ISO format", + ), +): + """ + Renews the chosen API key, reactivating it if it was revoked. + """ + return security_db_access.renew_key(api_key, expiration_date) + + +@router.get( + "/logs", + dependencies=[Depends(secret_based_security)], + response_model=UsageLogs, + include_in_schema=settings.security_hide_docs, +) +def get_api_key_usage_logs(): + """ + Returns usage information for all API keys + """ + # TODO Add some sort of filtering on older keys/unused keys? + + return UsageLogs( + logs=[ + UsageLog( + api_key=row[0], + is_active=row[1], + never_expire=row[2], + expiration_date=row[3], + latest_query_date=row[4], + total_queries=row[5], + name=row[6], + ) + for row in security_db_access.get_usage_stats() + ], + ) diff --git a/stream_fusion/web/api/docs/__init__.py b/stream_fusion/web/api/docs/__init__.py new file mode 100644 index 0000000..90a0ddd --- /dev/null +++ b/stream_fusion/web/api/docs/__init__.py @@ -0,0 +1,4 @@ +"""Routes for swagger and redoc.""" +from stream_fusion.web.api.docs.views import router + +__all__ = ["router"] diff --git a/stream_fusion/web/api/docs/views.py b/stream_fusion/web/api/docs/views.py new file mode 100644 index 0000000..d75854f --- /dev/null +++ b/stream_fusion/web/api/docs/views.py @@ -0,0 +1,53 @@ +from fastapi import APIRouter, Request +from fastapi.openapi.docs import ( + get_redoc_html, + get_swagger_ui_html, + get_swagger_ui_oauth2_redirect_html, +) +from fastapi.responses import HTMLResponse + +router = APIRouter() + + +@router.get("/docs", include_in_schema=False) +async def swagger_ui_html(request: Request) -> HTMLResponse: + """ + Swagger UI. + + :param request: current request. + :return: rendered swagger UI. + """ + title = request.app.title + return get_swagger_ui_html( + openapi_url=request.app.openapi_url, + title=f"{title} - Swagger UI", + oauth2_redirect_url=str(request.url_for("swagger_ui_redirect")), + swagger_js_url="/static/docs/swagger-ui-bundle.js", + swagger_css_url="/static/docs/swagger-ui.css", + ) + + +@router.get("/swagger-redirect", include_in_schema=False) +async def swagger_ui_redirect() -> HTMLResponse: + """ + Redirect to swagger. + + :return: redirect. + """ + return get_swagger_ui_oauth2_redirect_html() + + +@router.get("/redoc", include_in_schema=False) +async def redoc_html(request: Request) -> HTMLResponse: + """ + Redoc UI. + + :param request: current request. + :return: rendered redoc UI. + """ + title = request.app.title + return get_redoc_html( + openapi_url=request.app.openapi_url, + title=f"{title} - ReDoc", + redoc_js_url="/static/docs/redoc.standalone.js", + ) diff --git a/stream_fusion/web/api/monitoring/__init__.py b/stream_fusion/web/api/monitoring/__init__.py new file mode 100644 index 0000000..3529b58 --- /dev/null +++ b/stream_fusion/web/api/monitoring/__init__.py @@ -0,0 +1,4 @@ +"""API for checking project status.""" +from stream_fusion.web.api.monitoring.views import router + +__all__ = ["router"] diff --git a/stream_fusion/web/api/monitoring/views.py b/stream_fusion/web/api/monitoring/views.py new file mode 100644 index 0000000..1b36caf --- /dev/null +++ b/stream_fusion/web/api/monitoring/views.py @@ -0,0 +1,12 @@ +from fastapi import APIRouter + +router = APIRouter() + + +@router.get("/health") +def health_check() -> None: + """ + Checks the health of a project. + + It returns 200 if the project is healthy. + """ diff --git a/stream_fusion/web/api/router.py b/stream_fusion/web/api/router.py new file mode 100644 index 0000000..1ff73aa --- /dev/null +++ b/stream_fusion/web/api/router.py @@ -0,0 +1,7 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.api import auth, docs + +api_router = APIRouter() +api_router.include_router(auth.router, prefix="/auth", tags=["_auth"]) +api_router.include_router(docs.router) \ No newline at end of file diff --git a/stream_fusion/web/application.py b/stream_fusion/web/application.py new file mode 100644 index 0000000..ace8ccd --- /dev/null +++ b/stream_fusion/web/application.py @@ -0,0 +1,64 @@ +import os +from importlib import metadata +from pathlib import Path + +from fastapi import FastAPI +from fastapi.responses import UJSONResponse +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles + +from stream_fusion.logging_config import configure_logging + +from stream_fusion.version import get_version +from stream_fusion.web.api.router import api_router +from stream_fusion.web.root.router import root_router +from stream_fusion.web.playback.router import stream_router +from stream_fusion.web.lifetime import register_shutdown_event, register_startup_event + +APP_ROOT = Path(__file__).parent.parent + + +def get_app() -> FastAPI: + """ + Get FastAPI application. + + This is the main constructor of an application. + + :return: application. + """ + if not os.environ.get("RUNNING_UNDER_GUNICORN"): + configure_logging() + app = FastAPI( + title="StreamFusion", + version=str(get_version()), + docs_url=None, + redoc_url=None, + openapi_url="/api/openapi.json", + default_response_class=UJSONResponse, + ) + + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Adds startup and shutdown events. + register_startup_event(app) + register_shutdown_event(app) + + # Main router for the API. + app.include_router(router=root_router) + app.include_router(router=stream_router) + app.include_router(router=api_router, prefix="/api") + # Adds static directory. + # This directory is used to access swagger files. + app.mount( + "/static", + StaticFiles(directory=APP_ROOT / "static"), + name="static", + ) + + return app diff --git a/stream_fusion/web/lifetime.py b/stream_fusion/web/lifetime.py new file mode 100644 index 0000000..4a54d95 --- /dev/null +++ b/stream_fusion/web/lifetime.py @@ -0,0 +1,44 @@ +import os +from collections.abc import Awaitable +from typing import Callable + +from fastapi import FastAPI + +from stream_fusion.settings import settings + + +def register_startup_event( + app: FastAPI, +) -> Callable[[], Awaitable[None]]: # pragma: no cover + """ + Actions to run on application startup. + + This function uses fastAPI app to store data + in the state, such as db_engine. + + :param app: the fastAPI application. + :return: function that actually performs actions. + """ + + @app.on_event("startup") + async def _startup() -> None: + app.middleware_stack = None + app.middleware_stack = app.build_middleware_stack() + return _startup + + +def register_shutdown_event( + app: FastAPI, +) -> Callable[[], Awaitable[None]]: # pragma: no cover + """ + Actions to run on application's shutdown. + + :param app: fastAPI application. + :return: function that actually performs actions. + """ + + @app.on_event("shutdown") + async def _shutdown() -> None: + pass + + return _shutdown diff --git a/stream_fusion/web/playback/__init__.py b/stream_fusion/web/playback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stream_fusion/web/playback/router.py b/stream_fusion/web/playback/router.py new file mode 100644 index 0000000..a78f8b7 --- /dev/null +++ b/stream_fusion/web/playback/router.py @@ -0,0 +1,6 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.playback import stream + +stream_router = APIRouter() +stream_router.include_router(stream.router, prefix="/playback", tags=["stream"]) \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/__init__.py b/stream_fusion/web/playback/stream/__init__.py new file mode 100644 index 0000000..98080e6 --- /dev/null +++ b/stream_fusion/web/playback/stream/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.playback.stream.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/schemas.py b/stream_fusion/web/playback/stream/schemas.py new file mode 100644 index 0000000..2497869 --- /dev/null +++ b/stream_fusion/web/playback/stream/schemas.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel + +class StreamResponse(BaseModel): + content_range: str | None + content_length: str | None + accept_ranges: str + content_type: str + +class ErrorResponse(BaseModel): + detail: str + +class HeadResponse(BaseModel): + status_code: int \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/views.py b/stream_fusion/web/playback/stream/views.py new file mode 100644 index 0000000..53f71b7 --- /dev/null +++ b/stream_fusion/web/playback/stream/views.py @@ -0,0 +1,169 @@ +import asyncio +from functools import lru_cache +import aiohttp +from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status +from fastapi.responses import RedirectResponse, StreamingResponse + +from stream_fusion.services.redis.redis_config import get_redis_cache_dependency +from stream_fusion.utils.cache.local_redis import RedisCache +from stream_fusion.logging_config import logger +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service +from stream_fusion.utils.parse_config import parse_config +from stream_fusion.utils.string_encoding import decodeb64 +from stream_fusion.web.playback.stream.schemas import ( + ErrorResponse, + HeadResponse, + StreamResponse, +) + + +router = APIRouter() + + +@lru_cache(maxsize=128) +def get_adaptive_chunk_size(file_size): + MB = 1024 * 1024 + GB = 1024 * MB + + if file_size < 1 * GB: + return 1 * MB # 1 MB + elif file_size < 3 * GB: + return 2 * MB # 2 MB + elif file_size < 9 * GB: + return 5 * MB # 5 MB + elif file_size < 20 * GB: + return 10 * MB # 10 MB + else: + return 20 * MB # 20 MB + + +async def proxy_stream(url: str, headers: dict, proxy: str = None): + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=headers, proxy=proxy) as response: + file_size = int(response.headers.get("Content-Length", 0)) + chunk_size = get_adaptive_chunk_size(file_size) + + while True: + chunk = await response.content.read(chunk_size) + if not chunk: + break + yield chunk + + +def get_stream_link( + decoded_query: str, config: dict, ip: str, redis_cache: RedisCache +) -> str: + cache_key = f"stream_link:{decoded_query}_{ip}" + + cached_link = redis_cache.get(cache_key) + if cached_link: + logger.info(f"Stream link cached: {cached_link}") + return cached_link + + debrid_service = get_debrid_service(config) + link = debrid_service.get_stream_link(decoded_query, config, ip) + + redis_cache.set(cache_key, link, expiration=3600) # Cache for 1 hour + logger.info(f"Stream link generated and cached: {link}") + + return link + + +@router.get( + "/{config}/{query}", + response_model=StreamResponse, + responses={500: {"model": ErrorResponse}}, +) +async def get_playback( + config: str, + query: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +): + try: + if not query: + raise HTTPException(status_code=400, detail="Query required.") + + config = parse_config(config) + decoded_query = decodeb64(query) + ip = request.client.host + + link = get_stream_link(decoded_query, config, ip, redis_cache) + + range_header = request.headers.get("Range") + headers = {} + if range_header: + headers["Range"] = range_header + + proxy = None # Not yet implemented + + async with aiohttp.ClientSession() as session: + async with session.get(link, headers=headers, proxy=proxy) as response: + if response.status == 206: + return StreamingResponse( + proxy_stream(link, headers, proxy), + status_code=206, + headers=StreamResponse( + content_range=response.headers["Content-Range"], + content_length=response.headers["Content-Length"], + accept_ranges="bytes", + content_type="video/mp4", + ).model_dump(), + ) + elif response.status == 200: + return StreamingResponse( + proxy_stream(link, headers, proxy), + headers=StreamResponse( + content_range=None, + content_length=None, + accept_ranges="bytes", + content_type="video/mp4", + ).model_dump(), + ) + else: + return RedirectResponse(link, status_code=302) + + except Exception as e: + logger.error(f"Playback error: {e}") + raise HTTPException( + status_code=500, + detail=ErrorResponse( + detail="An error occurred while processing the request." + ).model_dump(), + ) + + +@router.head( + "/{config}/{query}", + response_model=HeadResponse, + responses={500: {"model": ErrorResponse}}, +) +async def head_playback( + config: str, + query: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +): + try: + if not query: + raise HTTPException(status_code=400, detail="Query required.") + + config = parse_config(config) + decoded_query = decodeb64(query) + ip = request.client.host + + cache_key = f"stream_link:{decoded_query}_{ip}" + + if redis_cache.exists(cache_key): + return Response(status_code=status.HTTP_200_OK) + else: + await asyncio.sleep(0.2) + return Response(status_code=status.HTTP_200_OK) + except Exception as e: + logger.error(f"HEAD request error: {e}") + return Response( + status=500, + content=ErrorResponse( + detail="An error occurred while processing the request." + ).model_dump_json(), + ) diff --git a/stream_fusion/web/root/__init__.py b/stream_fusion/web/root/__init__.py new file mode 100644 index 0000000..448fd33 --- /dev/null +++ b/stream_fusion/web/root/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.search import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/config/__init__.py b/stream_fusion/web/root/config/__init__.py new file mode 100644 index 0000000..2fbcce3 --- /dev/null +++ b/stream_fusion/web/root/config/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.config.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/config/schemas.py b/stream_fusion/web/root/config/schemas.py new file mode 100644 index 0000000..a57e24d --- /dev/null +++ b/stream_fusion/web/root/config/schemas.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class ManifestResponse(BaseModel): + id: str + icon: str + version: str + catalogs: List = Field(default_factory=list) + resources: List[str] + types: List[str] + name: str + description: str + behaviorHints: dict = Field(default_factory=lambda: { + "configurable": True, + }) + config: Optional[List[dict]] = None + +class ConfigureTemplateContext(BaseModel): + request: dict + +class StaticFileResponse(BaseModel): + file_path: str diff --git a/stream_fusion/web/root/config/views.py b/stream_fusion/web/root/config/views.py new file mode 100644 index 0000000..1413eb4 --- /dev/null +++ b/stream_fusion/web/root/config/views.py @@ -0,0 +1,78 @@ +from cachetools import TTLCache +from fastapi import APIRouter, Request +from fastapi.responses import FileResponse, RedirectResponse +from fastapi.templating import Jinja2Templates + +from stream_fusion.logging_config import logger +from stream_fusion.version import get_version +from stream_fusion.web.root.config.schemas import ManifestResponse, StaticFileResponse + +router = APIRouter() + +templates = Jinja2Templates(directory="/app/stream_fusion/templates") +stream_cache = TTLCache(maxsize=1000, ttl=3600) + + +@router.get("/") +async def root(): + logger.info("Redirecting to /configure") + return RedirectResponse(url="/configure") + + +@router.get("/configure") +@router.get("/{config}/configure") +async def configure(request: Request): + logger.info("Serving configuration page") + return templates.TemplateResponse("index.html", {"request": request}) + + +@router.get("/static/{file_path:path}", response_model=StaticFileResponse) +async def serve_static(file_path: str): + logger.debug(f"Serving static file: {file_path}") + return FileResponse(f"/app/stream_fusion/templates/{file_path}") + + +@router.get("/manifest.json") +async def get_manifest(): + logger.info("Serving manifest.json") + return ManifestResponse( + id="community.limedrive.streamfusion", + icon="https://i.imgur.com/tVjqEJP.png", + version=str(get_version()), + resources=["stream"], + types=["movie", "series"], + name="StreamFusion", + description="StreamFusion enhances Stremio by integrating torrent indexers and debrid services," + " providing access to a vast array of cached torrent sources. This plugin seamlessly bridges" + " Stremio with popular indexers and debrid platforms, offering users an expanded content" + " library and a smooth streaming experience.", + behaviorHints={ + "configurable": True, + "configurationRequired": True + }, + config=[ + { + "key": "api_key", + "title": "API Key", + "type": "text", + "required": True + } + ] + ) + + +@router.get("/{params}/manifest.json") +async def get_manifest(): + logger.info("Serving manifest.json") + return ManifestResponse( + id="community.limedrive.streamfusion", + icon="https://i.imgur.com/tVjqEJP.png", + version=str(get_version()), + resources=["stream"], + types=["movie", "series"], + name="StreamFusion", + description="StreamFusion enhances Stremio by integrating torrent indexers and debrid services," + " providing access to a vast array of cached torrent sources. This plugin seamlessly bridges" + " Stremio with popular indexers and debrid platforms, offering users an expanded content" + " library and a smooth streaming experience." + ) diff --git a/stream_fusion/web/root/router.py b/stream_fusion/web/root/router.py new file mode 100644 index 0000000..59d17ec --- /dev/null +++ b/stream_fusion/web/root/router.py @@ -0,0 +1,8 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.root import config +from stream_fusion.web.root import search + +root_router = APIRouter() +root_router.include_router(config.router, tags=["config"]) +root_router.include_router(search.router, tags=["search"]) \ No newline at end of file diff --git a/stream_fusion/web/root/search/__init__.py b/stream_fusion/web/root/search/__init__.py new file mode 100644 index 0000000..0db0c92 --- /dev/null +++ b/stream_fusion/web/root/search/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.search.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/search/schemas.py b/stream_fusion/web/root/search/schemas.py new file mode 100644 index 0000000..f6639c5 --- /dev/null +++ b/stream_fusion/web/root/search/schemas.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class Stream(BaseModel): + name: str + description: str + url: Optional[str] = None + infoHash: Optional[str] = None + fileIdx: Optional[int] = None + behaviorHints: dict = Field(default_factory=dict) + +class SearchResponse(BaseModel): + streams: List[Stream] \ No newline at end of file diff --git a/stream_fusion/web/root/search/stremio_parser.py b/stream_fusion/web/root/search/stremio_parser.py new file mode 100644 index 0000000..1800e0e --- /dev/null +++ b/stream_fusion/web/root/search/stremio_parser.py @@ -0,0 +1,141 @@ +import json +import queue +import threading +from typing import List + +from stream_fusion.utils.models.media import Media +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.string_encoding import encodeb64 + + +INSTANTLY_AVAILABLE = "[⚡]" +DOWNLOAD_REQUIRED = "[⬇️]" +DIRECT_TORRENT = "[🏴‍☠️]" + + +# TODO: Languages +def get_emoji(language): + emoji_dict = { + "fr": "🇫🇷 FRENCH", + "en": "🇬🇧 ENGLISH", + "es": "🇪🇸 SPANISH", + "de": "🇩🇪 GERMAN", + "it": "🇮🇹 ITALIAN", + "pt": "🇵🇹 PORTUGUESE", + "ru": "🇷🇺 RUSSIAN", + "in": "🇮🇳 INDIAN", + "nl": "🇳🇱 DUTCH", + "hu": "🇭🇺 HUNGARIAN", + "la": "🇲🇽 LATINO", + "multi": "🌍 MULTi", + } + return emoji_dict.get(language, "🇬🇧") + + +def filter_by_availability(item): + if item["name"].startswith(INSTANTLY_AVAILABLE): + return 0 + else: + return 1 + + +def filter_by_direct_torrnet(item): + if item["name"].startswith(DIRECT_TORRENT): + return 1 + else: + return 0 + + +def parse_to_debrid_stream(torrent_item: TorrentItem, configb64, host, torrenting, results: queue.Queue, media: Media): + if torrent_item.availability == True: + name = f"{INSTANTLY_AVAILABLE}\n" + else: + name = f"{DOWNLOAD_REQUIRED}\n" + + parsed_data = torrent_item.parsed_data.data + + resolution = parsed_data.resolution[0] if parsed_data.resolution else "Unknow" + name += f"{resolution}" + + if parsed_data.quality: + name += f"\n({'|'.join(parsed_data.quality)})" + + size_in_gb = round(int(torrent_item.size) / 1024 / 1024 / 1024, 2) + + title = f"{torrent_item.raw_title}\n" + + if torrent_item.file_name is not None: + title += f"{torrent_item.file_name}\n" + + title += f"👥 {torrent_item.seeders} 💾 {size_in_gb}GB 🔍 {torrent_item.indexer}\n" + + if parsed_data.codec: + title += f"🎥 {', '.join(parsed_data.codec)} " + if parsed_data.audio: + title += f"🎧 {', '.join(parsed_data.audio)} " + if parsed_data.codec or parsed_data.audio: + title += "\n" + + # Gestion des langues + if torrent_item.languages: + title += "/".join(get_emoji(language) for language in torrent_item.languages) + else: + title += "🌐" + + queryb64 = encodeb64(json.dumps(torrent_item.to_debrid_stream_query(media))).replace('=', '%3D') + + results.put({ + "name": name, + "description": title, + "url": f"{host}/playback/{configb64}/{queryb64}", + "behaviorHints":{ + "bingeGroup": f"stremio-jackett-{torrent_item.info_hash}", + "filename": torrent_item.file_name if torrent_item.file_name is not None else torrent_item.raw_title # TODO: Use parsed title? + } + }) + + if torrenting and torrent_item.privacy == "public": + name = f"{DIRECT_TORRENT}\n{parsed_data.quality}\n" + if len(parsed_data.quality) > 0 and parsed_data.quality[0] != "Unknown" and \ + parsed_data.quality[0] != "": + name += f"({'|'.join(parsed_data.quality)})" + results.put({ + "name": name, + "description": title, + "infoHash": torrent_item.info_hash, + "fileIdx": int(torrent_item.file_index) if torrent_item.file_index else None, + "behaviorHints":{ + "bingeGroup": f"stremio-jackett-{torrent_item.info_hash}", + "filename": torrent_item.file_name if torrent_item.file_name is not None else torrent_item.raw_title # TODO: Use parsed title? + } + # "sources": ["tracker:" + tracker for tracker in torrent_item.trackers] + }) + + +def parse_to_stremio_streams(torrent_items: List[TorrentItem], config, media): + stream_list = [] + threads = [] + thread_results_queue = queue.Queue() + + configb64 = encodeb64(json.dumps(config).replace('=', '%3D')) + for torrent_item in torrent_items[:int(config['maxResults'])]: + thread = threading.Thread(target=parse_to_debrid_stream, + args=(torrent_item, configb64, config['addonHost'], config['torrenting'], + thread_results_queue, media), + daemon=True) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + + while not thread_results_queue.empty(): + stream_list.append(thread_results_queue.get()) + + if len(stream_list) == 0: + return [] + + if config['debrid']: + stream_list = sorted(stream_list, key=filter_by_availability) + stream_list = sorted(stream_list, key=filter_by_direct_torrnet) + return stream_list diff --git a/stream_fusion/web/root/search/views.py b/stream_fusion/web/root/search/views.py new file mode 100644 index 0000000..6f42070 --- /dev/null +++ b/stream_fusion/web/root/search/views.py @@ -0,0 +1,255 @@ +import hashlib +import time +from fastapi import APIRouter, Depends, Request + +from stream_fusion.services.redis.redis_config import get_redis_cache_dependency +from stream_fusion.utils.cache.cache import search_public +from stream_fusion.utils.cache.local_redis import RedisCache +from stream_fusion.logging_config import logger +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service +from stream_fusion.utils.filter_results import ( + filter_items, + filter_out_non_matching_movies, + filter_out_non_matching_series, + merge_items, + sort_items, +) +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.jackett.jackett_service import JackettService +from stream_fusion.utils.metdata.cinemeta import Cinemeta +from stream_fusion.utils.metdata.tmdb import TMDB +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.parse_config import parse_config +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.web.root.search.schemas import SearchResponse, Stream +from stream_fusion.web.root.search.stremio_parser import parse_to_stremio_streams +from stream_fusion.utils.torrent.torrent_service import TorrentService +from stream_fusion.utils.torrent.torrent_smart_container import TorrentSmartContainer +from stream_fusion.utils.zilean.zilean_result import ZileanResult +from stream_fusion.utils.zilean.zilean_service import ZileanService + + +router = APIRouter() + + +@router.get("/{config}/stream/{stream_type}/{stream_id}", response_model=SearchResponse) +async def get_results( + config: str, + stream_type: str, + stream_id: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +) -> SearchResponse: + start = time.time() + logger.info(f"Stream request: {stream_type} - {stream_id}") + + stream_id = stream_id.replace(".json", "") + config = parse_config(config) + logger.debug(f"Parsed configuration: {config}") + + def get_metadata(): + logger.info(f"Fetching metadata from {config['metadataProvider']}") + if config["metadataProvider"] == "tmdb" and config["tmdbApi"]: + metadata_provider = TMDB(config) + else: + metadata_provider = Cinemeta(config) + return metadata_provider.get_metadata(stream_id, stream_type) + + media = redis_cache.get_or_set( + get_metadata, stream_id, stream_type, config["metadataProvider"] + ) + logger.info(f"Retrieved media metadata: {str(media.titles)}") + + def stream_cache_key(media): + if isinstance(media, Movie): + key_string = f"stream:{media.titles[0]}:{media.year}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"stream:{media.titles[0]}:{media.languages[0]}:{media.season}{media.episode}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + cached_result = redis_cache.get(stream_cache_key(media)) + if cached_result is not None: + logger.info("Returning cached processed results") + total_time = time.time() - start + logger.info(f"Request completed in {total_time:.2f} seconds") + return SearchResponse(streams=cached_result) + + debrid_service = get_debrid_service(config) + + def filter_all_results(results, media): + if media.type == "series": + filtered = filter_out_non_matching_series( + results, media.season, media.episode + ) + logger.info( + f"Filtered series results: {len(filtered)} (from {len(results)})" + ) + return filtered + else: + filtered = filter_out_non_matching_movies(results, media.year) + logger.info( + f"Filtered movie results: {len(filtered)} (from {len(results)})" + ) + return results + + def media_cache_key(media): + if isinstance(media, Movie): + key_string = f"media:{media.titles[0]}:{media.year}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"media:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + def get_search_results(media, config): + search_results = [] + torrent_service = TorrentService() + + def perform_search(update_cache=False): + nonlocal search_results + search_results = [] + + if config["cache"] and not update_cache: + public_cached_results = search_public(media) + if public_cached_results: + logger.info( + f"Found {len(public_cached_results)} public cached results" + ) + public_cached_results = [ + JackettResult().from_cached_item(torrent, media) + for torrent in public_cached_results + if len(torrent.get("hash", "")) == 40 + ] + public_cached_results = filter_items( + public_cached_results, media, config=config + ) + public_cached_results = torrent_service.convert_and_process( + public_cached_results + ) + search_results.extend(public_cached_results) + + if config["zilean"] and len(search_results) < int( + config["minCachedResults"] + ): + zilean_service = ZileanService(config) + zilean_search_results = zilean_service.search(media) + if zilean_search_results: + logger.info( + f"Found {len(zilean_search_results)} results from Zilean" + ) + zilean_search_results = [ + ZileanResult().from_api_cached_item(torrent, media) + for torrent in zilean_search_results + if len(torrent.get("infoHash", "")) == 40 + ] + zilean_search_results = filter_items( + zilean_search_results, media, config=config + ) + zilean_search_results = torrent_service.convert_and_process( + zilean_search_results + ) + search_results = merge_items(search_results, zilean_search_results) + + if config["jackett"] and len(search_results) < int( + config["minCachedResults"] + ): + jackett_service = JackettService(config) + jackett_search_results = jackett_service.search(media) + logger.info(f"Found {len(jackett_search_results)} results from Jackett") + filtered_jackett_search_results = filter_items( + jackett_search_results, media, config=config + ) + logger.info( + f"Filtered Jackett results: {len(filtered_jackett_search_results)}" + ) + if filtered_jackett_search_results: + torrent_results = torrent_service.convert_and_process( + filtered_jackett_search_results + ) + logger.debug( + f"Converted {len(torrent_results)} Jackett results to TorrentItems" + ) + search_results = merge_items(search_results, torrent_results) + + if update_cache and search_results: + logger.info(f"Updating cache with {len(search_results)} results") + try: + cache_key = media_cache_key(media) + search_results_dict = [item.to_dict() for item in search_results] + redis_cache.set(cache_key, search_results_dict) + except Exception as e: + logger.error(f"Error updating cache: {e}") + + perform_search() + return search_results + + def get_and_filter_results(media, config): + min_results = int(config.get("minCachedResults", 1)) + cache_key = media_cache_key(media) + + unfiltered_results = redis_cache.get(cache_key) + if unfiltered_results is None: + logger.info("No results in cache. Performing new search.") + nocache_results = get_search_results(media, config) + nocache_results_dict = [item.to_dict() for item in nocache_results] + redis_cache.set(cache_key, nocache_results_dict) + return nocache_results + else: + logger.info(f"Results retrieved from redis cache - {len(unfiltered_results)}.") + unfiltered_results = [TorrentItem.from_dict(item) for item in unfiltered_results] + + filtered_results = filter_all_results(unfiltered_results, media) + + if len(filtered_results) < min_results: + logger.info( + f"Insufficient filtered results ({len(filtered_results)}). Performing new search." + ) + redis_cache.delete(cache_key) + unfiltered_results = get_search_results(media, config) + unfiltered_results_dict = [item.to_dict() for item in unfiltered_results] + redis_cache.set(cache_key, unfiltered_results_dict) + filtered_results = filter_all_results(unfiltered_results, media) + + logger.info(f"Final number of filtered results: {len(filtered_results)}") + return filtered_results + + search_results = get_and_filter_results(media, config) + + def stream_processing(search_results, media, config): + torrent_smart_container = TorrentSmartContainer(search_results, media) + + if config["debrid"]: + hashes = torrent_smart_container.get_hashes() + ip = request.client.host + result = debrid_service.get_availability_bulk(hashes, ip) + torrent_smart_container.update_availability( + result, type(debrid_service), media + ) + logger.info(f"Checked availability for {len(result.items())} items") + + if config["cache"]: + logger.debug("Caching public container items") + torrent_smart_container.cache_container_items() + + best_matching_results = torrent_smart_container.get_best_matching() + best_matching_results = sort_items(best_matching_results, config) + logger.info(f"Found {len(best_matching_results)} best matching results") + + stream_list = parse_to_stremio_streams(best_matching_results, config, media) + logger.info(f"Processed {len(stream_list)} streams for Stremio") + + return stream_list + + stream_list = stream_processing(search_results, media, config) + streams = [Stream(**stream) for stream in stream_list] + redis_cache.set( + stream_cache_key(media), streams, expiration=3600 + ) # Make the cache expire after 1 hour TODO: Make this configurable + total_time = time.time() - start + logger.info(f"Request completed in {total_time:.2f} seconds") + return SearchResponse(streams=streams)