Repository: fazedordecodigo/PyFlunt (language: python)

Issue:
Release v2.2.0
## [2.2.0] - 2024-03-13
### Adicionado
- Validação UUID aplicando Duck Typing [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
- Novos cenários de teste unitários

### Corrigido
- Links do README quebrados [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
- Bug em `requires` que retornava falso positivo ao receber o valor booleano `False`.
- Reescrita dos testes unitários existentes.
[BUG] Links Quebrados
**Descrição do problema**
Alguns links do README estão quebrados


Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Messages standard
Introduce standardized messages in each validation.

Guide: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Localization/FluntErrorMessages.cs
Adicionar validação de UUID (GUID) 
## Nova Feature

- Validações de UUID (GUID)

Baseando-se na feature dessa lib: https://github.com/andrebaltieri/Flunt/blob/main/Flunt/Validations/GuidValidationContract.cs
Release v2.2.0
## [2.2.0] - 2024-03-13
### Adicionado
- Validação UUID aplicando Duck Typing [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
- Novos cenários de teste unitários

### Corrigido
- Links do README quebrados [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
- Bug em `requires` que retornava falso positivo ao receber o valor booleano `False`.
- Reescrita dos testes unitários existentes.
Release v2.2.0
## [2.2.0] - 2024-03-13
### Adicionado
- Validação UUID aplicando Duck Typing [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
- Novos cenários de teste unitários

### Corrigido
- Links do README quebrados [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
- Bug em `requires` que retornava falso positivo ao receber o valor booleano `False`.
- Reescrita dos testes unitários existentes.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index d914513..d863cbe 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -20,7 +20,7 @@ jobs:
         run: python -m pip install --upgrade pip
 
       - name: Instala dependências
-        run: python -m pip install -r requirements_dev.txt
+        run: python -m pip install -r ./requirements/requirements_dev.txt
 
       - name: Executa Ruff
         run: python -m ruff check
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 0db99ee..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-name: Realease
-
-on:
-  push:
-    branches:
-      - 'main'
-  pull_request:
-    types:
-      - closed
-    branches:
-      - 'main'
-  workflow_dispatch:
-    inputs:
-      number:
-        description: PR number
-        required: true
-
-jobs:
-  release:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: write
-    steps:
-    - name: Realiza o checkout
-      uses: actions/checkout@v4
-
-    - name: Set up Python
-      uses: actions/setup-python@v5
-
-    - name: Install dependencies
-      run: pip install toml
-
-    - name: Read version from pyproject.toml
-      id: read-version
-      run: |
-        version=$(python -c 'import toml; print(toml.load("pyproject.toml")["tool"]["poetry"]["version"])')
-        printf "LITELLM_VERSION=%s" "$version" >> $GITHUB_ENV
-
-    - name: Display version
-      run: echo "Current version is $LITELLM_VERSION"
-
-    - name: Create Release
-      id: create_release
-      uses: joutvhu/create-release@v1
-      with:
-        tag_name: v2.1.1
-        name: Release v2.1.1
-        body: |
-          ## [2.1.1] - 2024-02-27
-          ### Corrigido
-          - Links do README quebrados por @fazedordecodigo em #53
-          - Comandos para script de execução do pre-commit por @fazedordecodigo em #53
-          - Forma de comunicação para Reportar uma Vulnerabilidade por @fazedordecodigo em #53
-        draft: false
-        prerelease: false
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0c54a29..20cba67 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,22 +1,46 @@
+default_language_version:
+  python: python3
+
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v1.2.3
+  rev: v4.5.0
   hooks:
   - id: trailing-whitespace
   - id: end-of-file-fixer
   - id: check-yaml
   - id: check-added-large-files
+  - id: check-symlinks
+  - id: destroyed-symlinks
+  - id: mixed-line-ending
+    args:
+      - "--fix=lf"
+
+- repo: https://github.com/asottile/pyupgrade
+  rev: v3.15.1
+  hooks:
+    - id: pyupgrade
+      args: ["--py39-plus"]
+
 - repo: https://github.com/python-poetry/poetry
-  rev: 1.7.1
+  rev: 1.8.0
   hooks:
   - id: poetry-check
   - id: poetry-lock
   - id: poetry-export
-    args: ["--with", "dev", "-f", "requirements.txt", "-o", "requirements_dev.txt"]
-  - id: poetry-install
+    args: [
+      "--with",
+      "dev",
+      "-f",
+      "requirements.txt",
+      "-o",
+      "./requirements/requirements_dev.txt",
+    ]
+
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.2.1
+  rev: v0.3.0
   hooks:
   - id: ruff
-    args: [ --fix ]
-  - id: ruff-format
+    args:
+      - "--fix"
+      - "--fixable=ALL"
+      - "--exit-non-zero-on-fix"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23c4151..324a3c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-PORTUGUÊS | [ENGLISH](./CHANGELOG_EN.md)
+PORTUGUÊS | [ENGLISH](https://github.com/fazedordecodigo/PyFlunt/blob/main/CHANGELOG_EN.md)
 
 # Registro de Alterações
 
@@ -8,7 +8,6 @@ O formato é baseado em [Mantenha um Registro de Alterações](https://keepachan
 e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/spec/v2.0.0.html).
 
 ## [Não Lançado]
-- Adicionar validação de GUID [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
 - Adicionar mensagens padronizadas em PT-BR e EN [#18](https://github.com/fazedordecodigo/PyFlunt/issues/18)
 - Adicionar validação de DateTime [#27](https://github.com/fazedordecodigo/PyFlunt/issues/27)
 - Adicionar validação de Números [#28](https://github.com/fazedordecodigo/PyFlunt/issues/28)
@@ -18,6 +17,18 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR
 - Adicionar validação de Regex [#32](https://github.com/fazedordecodigo/PyFlunt/issues/32)
 - Adicionar validação de URL [#33](https://github.com/fazedordecodigo/PyFlunt/issues/33)
 
+## [2.2.0] - 2024-03-13
+### Adicionado
+- Validação UUID aplicando Duck Typing [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
+- Novos cenários de teste unitários
+
+### Corrigido
+- Links do README quebrados [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
+- Bug em `requires` que retornava falso positivo ao receber o valor booleano `False`.
+- Reescrita dos testes unitários existentes.
+
+
+
 ## [2.1.1] - 2024-02-27
 ### Corrigido
 - Links do README quebrados [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
@@ -78,6 +89,8 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR
 <br>
 <br>
 
+[2.2.0](https://github.com/fazedordecodigo/PyFlunt/compare/v2.1.1...v2.2.0)
+
 [2.1.1](https://github.com/fazedordecodigo/PyFlunt/compare/v2.1.0...v2.1.1)
 
 [2.1.0](https://github.com/fazedordecodigo/PyFlunt/compare/v2.0.0...v2.1.0)
diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md
index c8a4772..f8b2f26 100644
--- a/CHANGELOG_EN.md
+++ b/CHANGELOG_EN.md
@@ -1,4 +1,4 @@
-ENGLISH | [PORTUGUÊS](./CHANGELOG.md)
+ENGLISH | [PORTUGUÊS](https://github.com/fazedordecodigo/PyFlunt/blob/main/CHANGELOG.md)
 
 # Changelog
 
@@ -8,7 +8,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0
 and this project adheres to [Semantic Versioning](https://semver.org/lang/pt-BR/spec/v2.0.0.html).
 
 ## [Unreleased]
-- Add GUID validation [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
 - Add standardized messages in PT-BR and EN [#18](https://github.com/fazedordecodigo/PyFlunt/issues/18)
 - Add DateTime validation [#27](https://github.com/fazedordecodigo/PyFlunt/issues/27)
 - Add Numbers validation [#28](https://github.com/fazedordecodigo/PyFlunt/issues/28)
@@ -18,6 +17,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/pt-BR/
 - Add Regex validation [#32](https://github.com/fazedordecodigo/PyFlunt/issues/32)
 - Add URL validation [#33](https://github.com/fazedordecodigo/PyFlunt/issues/33)
 
+## [2.2.0] - 2024-03-13
+### Added
+- Applied Duck Typing for UUID validation [#11](https://github.com/fazedordecodigo/PyFlunt/issues/11)
+- New unit test scenarios
+
+### Fixed
+- Fixed broken links in the README [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
+- Fixed bug in `requires` that returned a false positive when receiving the boolean value `False`.
+- Rewrote existing unit tests.
+
 ## [2.1.1] - 2024-02-27
 ### Fixed
 - Broken README links [#53](https://github.com/fazedordecodigo/PyFlunt/issues/53)
@@ -40,7 +49,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/pt-BR/
 ### Fixed
 - Bug that prevented running on Python v3.9 [#36](https://github.com/fazedordecodigo/PyFlunt/issues/36)
 
-
 ## [2.0.0] - 2024-02-13
 ### Added
 
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 6a3d124..f9b7126 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,4 +1,4 @@
-PORTUGUÊS | [ENGLISH](./CODE_OF_CONDUCT_EN.md)
+PORTUGUÊS | [ENGLISH](https://github.com/fazedordecodigo/PyFlunt/blob/main/CODE_OF_CONDUCT_EN.md)
 
 # Código de Conduta do Pacto do Contribuidor
 
diff --git a/CODE_OF_CONDUCT_EN.md b/CODE_OF_CONDUCT_EN.md
index f5de1fb..bd52ccb 100644
--- a/CODE_OF_CONDUCT_EN.md
+++ b/CODE_OF_CONDUCT_EN.md
@@ -1,4 +1,4 @@
-ENGLISH | [PORTUGUÊS](./CODE_OF_CONDUCT.md)
+ENGLISH | [PORTUGUÊS](https://github.com/fazedordecodigo/PyFlunt/blob/main/CODE_OF_CONDUCT.md)
 
 # Contributor Covenant Code of Conduct
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d5ad3ed..69ccbcb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-PORTUGUÊS | [ENGLISH](./CONTRIBUTING_EN.md)
+PORTUGUÊS | [ENGLISH](https://github.com/fazedordecodigo/PyFlunt/blob/main/CONTRIBUTING_EN.md)
 
 # Contribuindo
 
@@ -92,7 +92,7 @@ $ poetry pre-commit-install
 
 ## Utilizando Localmente
 
-Agora você pode usá-lo [da mesma forma descrita no arquivo README.md](/README.md#utilização).
+Agora você pode usá-lo [da mesma forma descrita no arquivo README.md](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md#utilização).
 
 ## Testes
 
diff --git a/CONTRIBUTING_EN.md b/CONTRIBUTING_EN.md
index 12e995c..851cf94 100644
--- a/CONTRIBUTING_EN.md
+++ b/CONTRIBUTING_EN.md
@@ -1,4 +1,4 @@
-ENGLISH | [PORTUGUÊS](./CONTRIBUTING.md)
+ENGLISH | [PORTUGUÊS](https://github.com/fazedordecodigo/PyFlunt/blob/main/CONTRIBUTING.md)
 
 # Contributing
 
@@ -89,7 +89,7 @@ $ poetry pre-commit-install
 
 ## Using Locally
 
-Now you can use it [in the same way described in the README.md file](/README.md#utilization).
+Now you can use it [in the same way described in the README.md file](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md#utilization).
 
 ## Testing
 
diff --git a/README.md b/README.md
index 797b3c2..a6a9409 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,12 @@
-PORTUGUÊS | [ENGLISH](./README_EN.md)
+PORTUGUÊS | [ENGLISH](https://github.com/fazedordecodigo/PyFlunt/blob/main/README_EN.md)
 
 # 🐍 PyFlunt: Domain Notification Pattern
 
 Implementação Python inspirada no [Flunt](https://github.com/andrebaltieri/flunt) (.NET)
 
 [![Último Lançamento no PyPI](https://img.shields.io/pypi/v/flunt.svg)](https://pypi.org/project/flunt/)
-[![Downloads](https://pepy.tech/badge/flunt)](https://pepy.tech/project/flunt)
+[![python](https://img.shields.io/pypi/pyversions/flunt.svg)](https://pypi.org/project/flunt/)
+[![Downloads](https://static.pepy.tech/badge/flunt/month)](https://pepy.tech/project/flunt)
 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
 [![Discord](https://img.shields.io/discord/1211477389830393866?logo=discord&label=Discord&color=5865F2&logoColor=white)](https://discord.gg/HNwFHQWX)
 
@@ -35,8 +36,8 @@ pip install flunt
 ### 🔔 Notifiable
 
 ````python
-from flunt.notifiable import Notifiable
-from flunt.notification import Notification
+from flunt.notifications.notifiable import Notifiable
+from flunt.validations.contract import Contract
 
 class Nome(Notifiable):
     def __init__(self, nome):
@@ -53,8 +54,8 @@ class Nome(Notifiable):
 ### 📜 Contract
 ````python
 """Módulo Objetos de Valor."""
-from flunt.notifiable import Notifiable
-from flunt.contract import Contract
+from flunt.notifications.notifiable import Notifiable
+from flunt.validations.contract import Contract
 
 
 class Nome(Notifiable):
@@ -94,15 +95,15 @@ if not nome.is_valid():
 
 ## Contribuindo
 
-Consulte nosso DevGuide no link a seguir: [CONTRIBUTING](./CONTRIBUTING.md)
+Consulte nosso DevGuide no link a seguir: [CONTRIBUTING](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md/CONTRIBUTING.md)
 
 ## Registro de Alterações
 
-Consulte nosso registro de alterações no link a seguir: [CHANGELOG](./CHANGELOG.md)
+Consulte nosso registro de alterações no link a seguir: [CHANGELOG](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md/CHANGELOG.md)
 
 ## 📄 Licença
 
-Este projeto contém a licença MIT. Consulte o arquivo [LICENSE](./LICENSE).
+Este projeto contém a licença MIT. Consulte o arquivo [LICENSE](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md/LICENSE).
 
 ## Mods
 * [Flunt para C# (Original)](https://github.com/andrebaltieri/Flunt)
diff --git a/README_EN.md b/README_EN.md
index 8ab4db0..7866dd0 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -1,11 +1,12 @@
-ENGLISH | [PORTUGUÊS](README)
+ENGLISH | [PORTUGUÊS](https://github.com/fazedordecodigo/PyFlunt/blob/main/README.md)
 
 # 🐍 PyFlunt: Domain Notification Pattern
 
 Python implementation of Domain Notification Pattern inspired by [Flunt](https://github.com/andrebaltieri/flunt) (.NET)
 
 [![Último Lançamento no PyPI](https://img.shields.io/pypi/v/flunt.svg)](https://pypi.org/project/flunt/)
-[![Downloads](https://pepy.tech/badge/flunt)](https://pepy.tech/project/flunt)
+[![python](https://img.shields.io/pypi/pyversions/flunt.svg)](https://pypi.org/project/flunt/)
+[![Downloads](https://static.pepy.tech/badge/flunt/month)](https://pepy.tech/project/flunt)
 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
 [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://matrix.to/#/#pyflunt:gitter.im)
 
@@ -34,8 +35,8 @@ pip install flunt
 ### 🔔 Notifiable
 
 ````python
-from flunt.notifiable import Notifiable
-from flunt.notification import Notification
+from flunt.notifications.notifiable import Notifiable
+from flunt.validations.contract import Contract
 
 class Name(Notifiable):
     def __init__(self, name):
@@ -52,8 +53,8 @@ class Name(Notifiable):
 ### 📜 Contract
 ````python
 """Module Value Objects."""
-from flunt.notifiable import Notifiable
-from flunt.contract import Contract
+from flunt.notifications.notifiable import Notifiable
+from flunt.validations.contract import Contract
 
 
 class Name(Notifiable):
@@ -92,15 +93,15 @@ if not nome.is_valid():
 ````
 ## Contributing
 
-Please refer to our DevGuide at the following link: [CONTRIBUTING](CONTRIBUTING_EN)
+Please refer to our DevGuide at the following link: [CONTRIBUTING](https://github.com/fazedordecodigo/PyFlunt/blob/main/CONTRIBUTING_EN.md)
 
 ## Changelog
 
-Please refer to our changelog at the following link: [CHANGELOG](CHANGELOG_EN)
+Please refer to our changelog at the following link: [CHANGELOG](https://github.com/fazedordecodigo/PyFlunt/blob/main/CHANGELOG_EN.md)
 
 ## 📄 License
 
-This project contains the MIT license. See the file [LICENSE](LICENSE).
+This project contains the MIT license. See the file [LICENSE](https://github.com/fazedordecodigo/PyFlunt/blob/main/LICENSE.md).
 
 ## Mods
 * [Flunt for C# (Original)](https://github.com/andrebaltieri/Flunt)
diff --git a/SECURITY.md b/SECURITY.md
index 4f0332e..0d16f69 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,4 +1,4 @@
-PORTUGUÊS | [ENGLISH](./SECURITY_EN.md)
+PORTUGUÊS | [ENGLISH](https://github.com/fazedordecodigo/PyFlunt/blob/main/SECURITY_EN.md)
 
 # Política de Segurança
 
@@ -6,6 +6,9 @@ PORTUGUÊS | [ENGLISH](./SECURITY_EN.md)
 
 | Version | Supported          |
 | ------- | ------------------ |
+| 2.2.0   | :white_check_mark: |
+| 2.1.1   | :white_check_mark: |
+| 2.1.0   | :white_check_mark: |
 | 2.0.0   | :white_check_mark: |
 | 1.0.0   | :x:                |
 | 0.2.0   | :x:                |
diff --git a/SECURITY_EN.md b/SECURITY_EN.md
index 649f2b1..b2bc2ef 100644
--- a/SECURITY_EN.md
+++ b/SECURITY_EN.md
@@ -1,4 +1,4 @@
-ENGLISH | [PORTUGUÊS](./SECURITY.md)
+ENGLISH | [PORTUGUÊS](https://github.com/fazedordecodigo/PyFlunt/blob/main/SECURITY.md)
 
 # Security Policy
 
@@ -6,6 +6,9 @@ ENGLISH | [PORTUGUÊS](./SECURITY.md)
 
 | Version | Supported          |
 | ------- | ------------------ |
+| 2.2.0   | :white_check_mark: |
+| 2.1.1   | :white_check_mark: |
+| 2.1.0   | :white_check_mark: |
 | 2.0.0   | :white_check_mark: |
 | 1.0.0   | :x:                |
 | 0.2.0   | :x:                |
diff --git a/flunt/__init__.py b/flunt/__init__.py
index 636b259..e69de29 100644
--- a/flunt/__init__.py
+++ b/flunt/__init__.py
@@ -1,3 +0,0 @@
-"""Version Software."""
-
-__version__ = '1.0.0'
diff --git a/flunt/constants/__init__.py b/flunt/constants/__init__.py
new file mode 100644
index 0000000..f3379f1
--- /dev/null
+++ b/flunt/constants/__init__.py
@@ -0,0 +1,125 @@
+from .messages import (
+	CNPJ,
+	CONTAINS,
+	CONTAINS_ALL,
+	CONTAINS_KEY,
+	CONTAINS_KEYS,
+	CONTAINS_ONLY,
+	CPF,
+	CREDIT_CARD,
+	DATE,
+	DATE_AFTER,
+	DATE_BEFORE,
+	DATE_FORMAT,
+	EMAIL,
+	EQUALS,
+	EXACT_LENGTH,
+	GREATER_THAN,
+	GREATER_THAN_OR_EQUALS,
+	IN_LIST,
+	IP,
+	IS_CALLABLE,
+	IS_DECIMAL,
+	IS_DICT,
+	IS_EMPTY,
+	IS_FALSE,
+	IS_FLOAT,
+	IS_INSTANCE,
+	IS_INT,
+	IS_ITERABLE,
+	IS_LIST,
+	IS_NONE,
+	IS_NOT_CALLABLE,
+	IS_NOT_DECIMAL,
+	IS_NOT_DICT,
+	IS_NOT_EMPTY,
+	IS_NOT_FLOAT,
+	IS_NOT_INSTANCE,
+	IS_NOT_INT,
+	IS_NOT_ITERABLE,
+	IS_NOT_LIST,
+	IS_NOT_NONE,
+	IS_NOT_SET,
+	IS_NOT_STR,
+	IS_NOT_TYPE,
+	IS_NOT_UUID,
+	IS_SET,
+	IS_STR,
+	IS_TRUE,
+	IS_TYPE,
+	IS_UUID,
+	LESS_THAN,
+	LESS_THAN_OR_EQUALS,
+	MAC_ADDRESS,
+	MAX_LENGTH,
+	MIN_LENGTH,
+	NOT_EQUALS,
+	NOT_IN_LIST,
+	PHONE,
+	REGEX,
+	REQUIRED,
+	URL,
+)
+
+__all__ = [
+	'MAX_LENGTH',
+	'MIN_LENGTH',
+	'REQUIRED',
+	'EXACT_LENGTH',
+	'EMAIL',
+	'URL',
+	'MAC_ADDRESS',
+	'IP',
+	'REGEX',
+	'DATE',
+	'DATE_FORMAT',
+	'DATE_BEFORE',
+	'DATE_AFTER',
+	'CONTAINS',
+	'CONTAINS_ALL',
+	'CONTAINS_ONLY',
+	'CREDIT_CARD',
+	'CPF',
+	'CNPJ',
+	'PHONE',
+	'GREATER_THAN',
+	'GREATER_THAN_OR_EQUALS',
+	'LESS_THAN',
+	'LESS_THAN_OR_EQUALS',
+	'EQUALS',
+	'NOT_EQUALS',
+	'IN_LIST',
+	'NOT_IN_LIST',
+	'CONTAINS_KEY',
+	'CONTAINS_KEYS',
+	'IS_EMPTY',
+	'IS_NOT_EMPTY',
+	'IS_NONE',
+	'IS_NOT_NONE',
+	'IS_TRUE',
+	'IS_FALSE',
+	'IS_INSTANCE',
+	'IS_NOT_INSTANCE',
+	'IS_TYPE',
+	'IS_NOT_TYPE',
+	'IS_CALLABLE',
+	'IS_NOT_CALLABLE',
+	'IS_ITERABLE',
+	'IS_NOT_ITERABLE',
+	'IS_UUID',
+	'IS_NOT_UUID',
+	'IS_DECIMAL',
+	'IS_NOT_DECIMAL',
+	'IS_DICT',
+	'IS_NOT_DICT',
+	'IS_FLOAT',
+	'IS_NOT_FLOAT',
+	'IS_INT',
+	'IS_NOT_INT',
+	'IS_LIST',
+	'IS_NOT_LIST',
+	'IS_SET',
+	'IS_NOT_SET',
+	'IS_STR',
+	'IS_NOT_STR',
+]
diff --git a/flunt/constants/messages.py b/flunt/constants/messages.py
new file mode 100644
index 0000000..4dc4441
--- /dev/null
+++ b/flunt/constants/messages.py
@@ -0,0 +1,99 @@
+REQUIRED = 'The field {0} is required'
+MAX_LENGTH = 'The field {0} must be less than {1} characters'
+MIN_LENGTH = 'The field {0} must be at least {1} characters'
+EXACT_LENGTH = 'The field {0} must be exactly {1} characters'
+EMAIL = 'The field {0} must be a valid email'
+URL = 'The field {0} must be a valid URL'
+MAC_ADDRESS = 'The field {0} must be a valid MAC address'
+IP = 'The field {0} must be a valid IP address'
+REGEX = 'The field {0} is invalid'
+DATE = 'The field {0} must be a valid date'
+DATE_FORMAT = 'The field {0} must be a valid date in the format {1}'
+DATE_BEFORE = 'The field {0} must be a date before {1}'
+DATE_AFTER = 'The field {0} must be a date after {1}'
+CONTAINS = 'The field {0} must contain {1}'
+CONTAINS_ALL = 'The field {0} must contain all {1}'
+CONTAINS_ONLY = 'The field {0} must contain only {1}'
+CREDIT_CARD = 'The field {0} must be a valid credit card'
+CPF = 'The field {0} must be a valid CPF'
+CNPJ = 'The field {0} must be a valid CNPJ'
+PHONE = 'The field {0} must be a valid phone number'
+GREATER_THAN = 'The field {0} must be greater than {1}'
+GREATER_THAN_OR_EQUALS = 'The field {0} must be greater than or equals {1}'
+LESS_THAN = 'The field {0} must be less than {1}'
+LESS_THAN_OR_EQUALS = 'The field {0} must be less than or equals {1}'
+EQUALS = 'The field {0} must be equals {1}'
+NOT_EQUALS = 'The field {0} must be different than {1}'
+IN_LIST = 'The field {0} must be in the list {1}'
+NOT_IN_LIST = 'The field {0} must not be in the list {1}'
+CONTAINS_KEY = 'The field {0} must contain the key {1}'
+CONTAINS_KEYS = 'The field {0} must contain the keys {1}'
+IS_EMPTY = 'The field {0} must be empty'
+IS_NOT_EMPTY = 'The field {0} must not be empty'
+IS_NONE = 'The field {0} must be None'
+IS_NOT_NONE = 'The field {0} must not be None'
+IS_TRUE = 'The field {0} must be True'
+IS_FALSE = 'The field {0} must be False'
+IS_INSTANCE = 'The field {0} must be an instance of {1}'
+IS_NOT_INSTANCE = 'The field {0} must not be an instance of {1}'
+IS_TYPE = 'The field {0} must be a type of {1}'
+IS_NOT_TYPE = 'The field {0} must not be a type of {1}'
+IS_CALLABLE = 'The field {0} must be callable'
+IS_NOT_CALLABLE = 'The field {0} must not be callable'
+IS_ITERABLE = 'The field {0} must be iterable'
+IS_NOT_ITERABLE = 'The field {0} must not be iterable'
+IS_UUID = 'The field {0} must be a valid UUID'
+IS_NOT_UUID = 'The field {0} must not be a valid UUID'
+IS_DECIMAL = 'The field {0} must be a valid Decimal'
+IS_NOT_DECIMAL = 'The field {0} must not be a valid Decimal'
+IS_DICT = 'The field {0} must be a valid Dict'
+IS_NOT_DICT = 'The field {0} must not be a valid Dict'
+IS_FLOAT = 'The field {0} must be a valid Float'
+IS_NOT_FLOAT = 'The field {0} must not be a valid Float'
+IS_INT = 'The field {0} must be a valid Int'
+IS_NOT_INT = 'The field {0} must not be a valid Int'
+IS_LIST = 'The field {0} must be a valid List'
+IS_NOT_LIST = 'The field {0} must not be a valid List'
+IS_SET = 'The field {0} must be a valid Set'
+IS_NOT_SET = 'The field {0} must not be a valid Set'
+IS_STR = 'The field {0} must be a valid Str'
+IS_NOT_STR = 'The field {0} must not be a valid Str'
+IS_TUPLE = 'The field {0} must be a valid Tuple'
+IS_NOT_TUPLE = 'The field {0} must not be a valid Tuple'
+IS_JSON = 'The field {0} must be a valid JSON'
+IS_NOT_JSON = 'The field {0} must not be a valid JSON'
+IS_URL = 'The field {0} must be a valid URL'
+IS_NOT_URL = 'The field {0} must not be a valid URL'
+IS_EMAIL = 'The field {0} must be a valid Email'
+IS_NOT_EMAIL = 'The field {0} must not be a valid Email'
+IS_MAC_ADDRESS = 'The field {0} must be a valid MAC Address'
+IS_NOT_MAC_ADDRESS = 'The field {0} must not be a valid MAC Address'
+IS_IP = 'The field {0} must be a valid IP Address'
+IS_NOT_IP = 'The field {0} must not be a valid IP Address'
+IS_CREDIT_CARD = 'The field {0} must be a valid Credit Card'
+IS_NOT_CREDIT_CARD = 'The field {0} must not be a valid Credit Card'
+IS_CPF = 'The field {0} must be a valid CPF'
+IS_NOT_CPF = 'The field {0} must not be a valid CPF'
+IS_CNPJ = 'The field {0} must be a valid CNPJ'
+IS_NOT_CNPJ = 'The field {0} must not be a valid CNPJ'
+IS_PHONE = 'The field {0} must be a valid Phone'
+IS_NOT_PHONE = 'The field {0} must not be a valid Phone'
+IS_DATE = 'The field {0} must be a valid Date'
+IS_NOT_DATE = 'The field {0} must not be a valid Date'
+IS_DATE_FORMAT = 'The field {0} must be a valid Date in the format {1}'
+IS_NOT_DATE_FORMAT = 'The field {0} must not be a valid Date in the format {1}'
+IS_DATE_BEFORE = 'The field {0} must be a Date before {1}'
+IS_NOT_DATE_BEFORE = 'The field {0} must not be a Date before {1}'
+IS_DATE_AFTER = 'The field {0} must be a Date after {1}'
+IS_NOT_DATE_AFTER = 'The field {0} must not be a Date after {1}'
+IS_DATE_BETWEEN = 'The field {0} must be a Date between {1} and {2}'
+IS_NOT_DATE_BETWEEN = 'The field {0} must not be a Date between {1} and {2}'
+IS_DATE_IN = 'The field {0} must be a Date in {1}'
+IS_NOT_DATE_IN = 'The field {0} must not be a Date in {1}'
+IS_DATE_NOT_IN = 'The field {0} must not be a Date in {1}'
+IS_DATE_NOT_BETWEEN = 'The field {0} must not be a Date between {1} and {2}'
+IS_DATE_NOT_BEFORE = 'The field {0} must not be a Date before {1}'
+IS_DATE_NOT_AFTER = 'The field {0} must not be a Date after {1}'
+IS_DATE_NOT_EQUAL = 'The field {0} must not be a Date equal to {1}'
+IS_DATE_EQUAL = 'The field {0} must be a Date equal to {1}'
+IS_DATE_NOT_EQUAL = 'The field {0} must not be a Date equal to {1}'
diff --git a/flunt/localization/flunt_error_messages.py b/flunt/localization/flunt_error_messages.py
new file mode 100644
index 0000000..e3e0584
--- /dev/null
+++ b/flunt/localization/flunt_error_messages.py
@@ -0,0 +1,30 @@
+from flunt.constants import MAX_LENGTH, REQUIRED
+
+
+def required(field: str):
+	"""
+	Returns the error message for a required field.
+
+	Args:
+	    field (str): The name of the required field.
+
+	Returns:
+	    str: The error message.
+
+	"""
+	return REQUIRED.format(field)
+
+
+def max_length(field: str, max: int):
+	"""
+	Returns a formatted error message for a maximum length validation.
+
+	Args:
+	    field (str): The name of the field being validated.
+	    max (int): The maximum length allowed for the field.
+
+	Returns:
+	    str: The formatted error message.
+
+	"""
+	return MAX_LENGTH.format(field, max)
diff --git a/flunt/validations/commons_validation_contract.py b/flunt/validations/commons_validation_contract.py
new file mode 100644
index 0000000..2b62819
--- /dev/null
+++ b/flunt/validations/commons_validation_contract.py
@@ -0,0 +1,554 @@
+"""Module Contract."""
+
+from collections.abc import Iterable
+from decimal import Decimal
+from struct import Struct
+from typing import Callable, Union, overload
+from uuid import UUID
+
+from typing_extensions import Self
+
+from flunt.notifications.notifiable import Notifiable
+from flunt.notifications.notification import Notification
+
+
+class CommonsValidationContract(Notifiable):
+	"""
+	Class for validating values and adding notifications based on various comparisons.
+
+	Parameters
+	----------
+	    N/A
+
+	Attributes:
+	----------
+	    N/A
+
+	Methods:
+	-------
+	- is_none(value: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], key: str, message: str): Checks if a value is None.
+	- is_not_none(value: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], key: str, message: str): Checks if a value is not None.
+	- are_equals(value: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], comparer: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], key: str, message: str): Checks if two values are equal.
+	- are_not_equals(value: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], comparer: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct], key: str, message: str): Checks if two values are not equal.
+
+	"""
+
+	@overload
+	def is_none(self, value: tuple, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: Struct, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: set, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: bool, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: dict, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: list, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: Iterable, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: Callable, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: int, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: str, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: Decimal, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: float, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: UUID, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_none(self, value: object, key: str, message: str) -> Self:
+		...
+
+	def is_none(
+		self,
+		value: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		key: str,
+		message: str,
+	) -> Self:
+		"""
+		Check if value is not None and adds a notification if it is.
+
+		Parameters
+		----------
+		`value`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The value to be checked.
+		`key`: str
+		    The key or identifier associated with the check.
+		`message`: str
+		    The notification message to be added if the value is None.
+
+		Returns:
+		-------
+		`self`
+		    The current instance with potential notifications added.
+
+		Notes:
+		-----
+		- If the `value` is not None, no notification is added.
+		- If the `value` is None, a notification is added to the current instance with the provided `key` and `message`.
+
+		Examples:
+		--------
+		```python
+		obj = Contract()
+		      .is_none("Hello", "ValueCheck", "Value should not be None")
+		obj.is_valid
+		```
+
+		"""
+		if value is not None:
+			self.add_notification(Notification(key, message))
+
+		return self
+
+	@overload
+	def is_not_none(self, value: tuple, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: Struct, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: set, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: bool, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: dict, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: list, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: Iterable, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: Callable, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: int, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: str, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: Decimal, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: float, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: UUID, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def is_not_none(self, value: object, key: str, message: str) -> Self:
+		...
+
+	def is_not_none(
+		self,
+		value: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		key: str,
+		message: str,
+	) -> Self:
+		"""
+		Check if a value is not None and adds a notification if it is.
+
+		Parameters
+		----------
+		`value`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The value to be checked.
+		`key`: str
+		    The key or identifier associated with the check.
+		`message`: str
+		    The notification message to be added if the value is None.
+
+		Returns:
+		-------
+		`self`
+		    The current instance with potential notifications added.
+
+		Notes:
+		-----
+		- If the `value` is None, a notification is added to the current instance with the provided `key` and `message`.
+		- If the `value` is not None, no notification is added.
+
+		Examples:
+		--------
+		```python
+		obj = Contract()
+		      .is_not_none("Hello", "ValueCheck", "Value should not be None")
+		obj.is_valid
+		```
+
+		"""
+		if value is None:
+			self.add_notification(Notification(key, message))
+
+		return self
+
+	@overload
+	def are_equals(self, value: tuple, comparer: tuple, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(
+		self, value: Struct, comparer: Struct, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: set, comparer: set, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: bool, comparer: bool, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: dict, comparer: dict, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: list, comparer: list, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(
+		self, value: Iterable, comparer: Iterable, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_equals(
+		self, value: Callable, comparer: Callable, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: int, comparer: int, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: str, comparer: str, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(
+		self, value: Decimal, comparer: Decimal, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: float, comparer: float, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(self, value: UUID, comparer: UUID, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_equals(
+		self, value: object, comparer: object, key: str, message: str
+	) -> Self:
+		...
+
+	def are_equals(
+		self,
+		value: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		comparer: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		key: str,
+		message: str,
+	) -> Self:
+		"""
+		Check if two string values are equal and adds a notification if they are not equal.
+
+		Parameters
+		----------
+		`value`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The first value to compare.
+		`comparer`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The second value to compare with the first value.
+		`key`: str
+		    The key or identifier associated with the comparison.
+		`message`: str
+		    The notification message to be added if the values are not equal.
+
+		Returns:
+		-------
+		`self`
+		    The current instance with potential notifications added.
+
+		Notes:
+		-----
+		- If the `value` is not equal to `comparer`, a notification is added to the current instance
+		with the provided `key` and `message`. Otherwise, no notification is added.
+
+		Examples:
+		--------
+		```python
+		obj = Contract()
+		      .are_equals("Hello", "Hello", "Comparison", "Values should be equal")
+		obj.is_valid
+		```
+
+		"""
+		if value != comparer:
+			self.add_notification(Notification(key, message))
+
+		return self
+
+	@overload
+	def are_not_equals(
+		self, value: tuple, comparer: tuple, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: Struct, comparer: Struct, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(self, value: set, comparer: set, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: bool, comparer: bool, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: dict, comparer: dict, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: list, comparer: list, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: Iterable, comparer: Iterable, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: Callable, comparer: Callable, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(self, value: int, comparer: int, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_not_equals(self, value: str, comparer: str, key: str, message: str) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: Decimal, comparer: Decimal, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: float, comparer: float, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: UUID, comparer: UUID, key: str, message: str
+	) -> Self:
+		...
+
+	@overload
+	def are_not_equals(
+		self, value: object, comparer: object, key: str, message: str
+	) -> Self:
+		...
+
+	def are_not_equals(
+		self,
+		value: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		comparer: Union[
+			str,
+			int,
+			list,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+			bool,
+		],
+		key: str,
+		message: str,
+	) -> Self:
+		"""
+		Require two values are not equals.
+
+		Parameters
+		----------
+		`value`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The value to be compared.
+		`comparer`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The value to compare with `value`.
+		`key`: str
+		    The key or identifier related to the comparison.
+		`message`: str
+		    The notification message in case of equal values.
+
+		Returns:
+		-------
+		`self`
+		    The current instance with potential notifications added.
+
+		Notes:
+		-----
+		- If the `value` is equal to `comparer`, a notification is added to the current instance
+		with the provided `key` and `message`. Otherwise, no notification is added.
+
+		Examples:
+		--------
+		```python
+		obj = Contract()
+		      .are_not_equals("Hello", "World", "Comparison", "Values should not be equal")
+		obj.is_valid
+		```
+
+		"""
+		if value == comparer:
+			self.add_notification(Notification(key, message))
+
+		return self
diff --git a/flunt/validations/contract.py b/flunt/validations/contract.py
index f70fb51..8fc530e 100644
--- a/flunt/validations/contract.py
+++ b/flunt/validations/contract.py
@@ -1,10 +1,17 @@
 """Module Contract."""
 
+from collections.abc import Iterable
+from decimal import Decimal
+from struct import Struct
+from typing import Callable, Union, overload
+from uuid import UUID
+
 from typing_extensions import Self
 
 from flunt.notifications.notifiable import Notifiable
 from flunt.notifications.notification import Notification
 from flunt.validations.bool_validation_contract import BoolValidationContract
+from flunt.validations.commons_validation_contract import CommonsValidationContract
 from flunt.validations.credit_card_validation_contract import (
 	CreditCardValidationContract,
 )
@@ -17,10 +24,11 @@ class Contract(
 	EmailValidationContract,
 	BoolValidationContract,
 	CreditCardValidationContract,
+	CommonsValidationContract,
 	Notifiable,
 ):
 	"""
-	Class repesents a contract for validating data..
+	Class represents a contract for validating data..
 
 	Parameters:
 	-----------
@@ -32,18 +40,94 @@ class Contract(
 
 	Methods:
 	-------
-	- requires(value: str, key: str, message: str): Checks if the given value is empty and adds a notification if it is.
+	- requires(value, key: str, message: str): Checks if the given value is empty and adds a notification if it is.
 
 	"""
 
-	def requires(self, value: str, key: str, message: str) -> Self:
+	@overload
+	def requires(self, value: tuple, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: Struct, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: set, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: bool, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: dict, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: list, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: Iterable, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: Callable, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: int, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: str, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: Decimal, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: float, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: UUID, key: str, message: str):
+		...
+
+	@overload
+	def requires(self, value: object, key: str, message: str):
+		...
+
+	def requires(
+		self,
+		value: Union[
+			str,
+			int,
+			list,
+			bool,
+			Decimal,
+			float,
+			UUID,
+			dict,
+			object,
+			set,
+			Struct,
+			tuple,
+			Iterable,
+			Callable,
+		],
+		key: str,
+		message: str,
+	) -> Self:
 		"""
 		Check if the given value is empty and adds a notification if it is.
 
 		Parameters:
 		-----------
-		`value`: str
-		    The string value to be checked.
+		`value`: [bool | str | float | int | Tuple | Set | List | Iterable | Dict | Callable | Decimal | UUID | object | Struct]
+		    The value to be checked.
 		`key`: str
 		    The key or identifier associated with the notification.
 		`message`: str
@@ -63,16 +147,12 @@ def requires(self, value: str, key: str, message: str) -> Self:
 		Examples:
 		--------
 		```python
-		contract = (
-		    Contract().requires(
-		        '', 'key', 'message'
-		    )
-		)
+		contract = Contract().requires('', 'key', 'message')
 		contract.is_valid  # False
 		```
 
 		"""
-		if not value:
+		if not value and not isinstance(value, bool):
 			self.add_notification(Notification(key, message))
 
 		return self
diff --git a/flunt/validations/email_validation_contract.py b/flunt/validations/email_validation_contract.py
index ddfd400..63eaa4d 100644
--- a/flunt/validations/email_validation_contract.py
+++ b/flunt/validations/email_validation_contract.py
@@ -1,8 +1,9 @@
 """Module Email Validation Contract."""
 
 import re
+from typing import Union
 
-from typing_extensions import Self, Union
+from typing_extensions import Self
 
 from flunt.localization.flunt_regex_patterns import FluntRegexPatterns
 from flunt.notifications.notifiable import Notifiable
diff --git a/flunt/validations/strings_validation_contract.py b/flunt/validations/strings_validation_contract.py
index ae7bf8a..0204b8e 100644
--- a/flunt/validations/strings_validation_contract.py
+++ b/flunt/validations/strings_validation_contract.py
@@ -24,11 +24,7 @@ class StringValidationContract(Notifiable):
 	- is_lower_or_equals_than(value, comparer, key, message): Checks if the length of a string value is lower or equal to a given number.
 	- is_greater_than(value, comparer, key, message): Checks if the length of a string value is greater than a given number.
 	- is_greater_or_equals_than(value, comparer, key, message): Checks if the length of a string value is greater or equal to a given number.
-	- is_none(value, key, message): Checks if a string value is None.
-	- is_not_none(value, key, message): Checks if a string value is not None.
 	- is_not_none_or_white_space(value, key, message): Checks if a string value is not None or whitespace.
-	- are_equals(value, comparer, key, message): Checks if two string values are equal.
-	- are_not_equals(value, comparer, key, message): Checks if two string values are not equal.
 	- contains(value, comparer, key, message): Checks if a string contains a specific substring.
 	- not_contains(value, comparer, key, message): Checks if a string does not contain a specific substring.
 	- is_between(value, min, max, key, message): Checks if the length of a string is between a minimum and maximum value.
@@ -213,80 +209,6 @@ def is_greater_or_equals_than(
 
 		return self
 
-	def is_none(self, value: str, key: str, message: str) -> Self:
-		"""
-		Check if a string value is not None and adds a notification if it is.
-
-		Parameters
-		----------
-		`value`: str
-		    The string value to be checked.
-		`key`: str
-		    The key or identifier associated with the check.
-		`message`: str
-		    The notification message to be added if the value is None.
-
-		Returns:
-		-------
-		`self`
-		    The current instance with potential notifications added.
-
-		Notes:
-		-----
-		- If the `value` is not None, no notification is added.
-		- If the `value` is None, a notification is added to the current instance with the provided `key` and `message`.
-
-		Examples:
-		--------
-		```python
-		obj = Contract()
-		      .is_none("Hello", "ValueCheck", "Value should not be None")
-		obj.is_valid
-		```
-
-		"""
-		if value is not None:
-			self.add_notification(Notification(key, message))
-
-		return self
-
-	def is_not_none(self, value: str, key: str, message: str) -> Self:
-		"""
-		Check if a string value is not None and adds a notification if it is.
-
-		Parameters
-		----------
-		`value`: str
-		    The string value to be checked.
-		`key`: str
-		    The key or identifier associated with the check.
-		`message`: str
-		    The notification message to be added if the value is None.
-
-		Returns:
-		-------
-		`self`
-		    The current instance with potential notifications added.
-
-		Notes:
-		-----
-		- If the `value` is None, a notification is added to the current instance with the provided `key` and `message`.
-		- If the `value` is not None, no notification is added.
-
-		Examples:
-		--------
-		```python
-		obj = Contract()
-		      .is_not_none("Hello", "ValueCheck", "Value should not be None")
-		obj.is_valid
-		```
-
-		"""
-		if value is None:
-			self.add_notification(Notification(key, message))
-
-		return self
-
 	def is_not_none_or_white_space(self, value: str, key: str, message: str) -> Self:
 		"""
 		Check if a string value is not None or whitespace and adds a notification if it is.
@@ -325,84 +247,6 @@ def is_not_none_or_white_space(self, value: str, key: str, message: str) -> Self
 
 		return self
 
-	def are_equals(self, value: str, comparer: str, key: str, message: str) -> Self:
-		"""
-		Check if two string values are equal and adds a notification if they are not equal.
-
-		Parameters
-		----------
-		`value`: str
-		    The first string value to compare.
-		`comparer`: str
-		    The second string value to compare with the first value.
-		`key`: str
-		    The key or identifier associated with the comparison.
-		`message`: str
-		    The notification message to be added if the values are not equal.
-
-		Returns:
-		-------
-		`self`
-		    The current instance with potential notifications added.
-
-		Notes:
-		-----
-		- If the `value` is not equal to `comparer`, a notification is added to the current instance
-		with the provided `key` and `message`. Otherwise, no notification is added.
-
-		Examples:
-		--------
-		```python
-		obj = Contract()
-		      .are_equals("Hello", "Hello", "Comparison", "Values should be equal")
-		obj.is_valid
-		```
-
-		"""
-		if value != comparer:
-			self.add_notification(Notification(key, message))
-
-		return self
-
-	def are_not_equals(self, value: str, comparer: str, key: str, message: str) -> Self:
-		"""
-		Require two strings are not equals.
-
-		Parameters
-		----------
-		`value`: str
-		    The value to be compared.
-		`comparer`: str
-		    The value to compare with `value`.
-		`key`: str
-		    The key or identifier related to the comparison.
-		`message`: str
-		    The notification message in case of equal values.
-
-		Returns:
-		-------
-		`self`
-		    The current instance with potential notifications added.
-
-		Notes:
-		-----
-		- If the `value` is equal to `comparer`, a notification is added to the current instance
-		with the provided `key` and `message`. Otherwise, no notification is added.
-
-		Examples:
-		--------
-		```python
-		obj = Contract()
-		      .are_not_equals("Hello", "World", "Comparison", "Values should not be equal")
-		obj.is_valid
-		```
-
-		"""
-		if value == comparer:
-			self.add_notification(Notification(key, message))
-
-		return self
-
 	def contains(self, value: str, comparer: str, key: str, message: str) -> Self:
 		"""
 		Check if a string value contains another string and adds a notification if it does.
@@ -438,7 +282,7 @@ def contains(self, value: str, comparer: str, key: str, message: str) -> Self:
 		```
 
 		"""
-		if value.find(comparer) > -1:
+		if value.find(comparer) == -1:
 			self.add_notification(Notification(key, message))
 
 		return self
@@ -478,7 +322,7 @@ def not_contains(self, value: str, comparer: str, key: str, message: str) -> Sel
 		```
 
 		"""
-		if value.find(comparer) == -1:
+		if value.find(comparer) > -1:
 			self.add_notification(Notification(key, message))
 
 		return self
diff --git a/poetry.lock b/poetry.lock
index 1db3d0c..f04a2fe 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.0 and should not be changed by hand.
 
 [[package]]
 name = "babel"
@@ -16,18 +16,18 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
 
 [[package]]
 name = "build"
-version = "1.0.3"
+version = "1.1.1"
 description = "A simple, correct Python build frontend"
 optional = false
 python-versions = ">= 3.7"
 files = [
-    {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"},
-    {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"},
+    {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"},
+    {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"},
 ]
 
 [package.dependencies]
 colorama = {version = "*", markers = "os_name == \"nt\""}
-importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
+importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""}
 packaging = ">=19.0"
 pyproject_hooks = "*"
 tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
@@ -438,6 +438,23 @@ ssh = ["bcrypt (>=3.1.5)"]
 test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
 test-randomorder = ["pytest-randomly"]
 
+[[package]]
+name = "deprecated"
+version = "1.2.14"
+description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
+    {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
+]
+
+[package.dependencies]
+wrapt = ">=1.10,<2"
+
+[package.extras]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
+
 [[package]]
 name = "distlib"
 version = "0.3.8"
@@ -613,13 +630,13 @@ dev = ["flake8", "markdown", "twine", "wheel"]
 
 [[package]]
 name = "griffe"
-version = "0.41.0"
+version = "0.42.0"
 description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "griffe-0.41.0-py3-none-any.whl", hash = "sha256:8aa7fc6eb00cb80af9c0198178c6b7110cb59fa2c5187bb13ea25eebbe4dd928"},
-    {file = "griffe-0.41.0.tar.gz", hash = "sha256:850128c3198c18713eaf0a6cc8572e590a16b1965f72a4e871e66cf84740903f"},
+    {file = "griffe-0.42.0-py3-none-any.whl", hash = "sha256:384df6b802a60f70e65fdb7e83f5b27e2da869a12eac85b25b55250012dbc263"},
+    {file = "griffe-0.42.0.tar.gz", hash = "sha256:fb83ee602701ffdf99c9a6bf5f0a5a3bd877364b3bffb2c451dc8fbd9645b0cf"},
 ]
 
 [package.dependencies]
@@ -652,22 +669,22 @@ files = [
 
 [[package]]
 name = "importlib-metadata"
-version = "7.0.1"
+version = "7.0.2"
 description = "Read metadata from Python packages"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
-    {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
+    {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"},
+    {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"},
 ]
 
 [package.dependencies]
 zipp = ">=0.5"
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
 
 [[package]]
 name = "iniconfig"
@@ -895,13 +912,13 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-imp
 
 [[package]]
 name = "mkdocs-autorefs"
-version = "1.0.0"
+version = "1.0.1"
 description = "Automatically link across pages in MkDocs."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "mkdocs_autorefs-1.0.0-py3-none-any.whl", hash = "sha256:2b6d288f0582589d1be7c99ce4470c8e7c5077892014051ff0d4ff574a73dbe8"},
-    {file = "mkdocs_autorefs-1.0.0.tar.gz", hash = "sha256:1b20db41cade632b07b7a73dee818977b52e869c9deb438f6f20e2896ff01859"},
+    {file = "mkdocs_autorefs-1.0.1-py3-none-any.whl", hash = "sha256:aacdfae1ab197780fb7a2dac92ad8a3d8f7ca8049a9cbe56a4218cd52e8da570"},
+    {file = "mkdocs_autorefs-1.0.1.tar.gz", hash = "sha256:f684edf847eced40b570b57846b15f0bf57fb93ac2c510450775dcf16accb971"},
 ]
 
 [package.dependencies]
@@ -911,13 +928,13 @@ mkdocs = ">=1.1"
 
 [[package]]
 name = "mkdocs-material"
-version = "9.5.11"
+version = "9.5.13"
 description = "Documentation that simply works"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "mkdocs_material-9.5.11-py3-none-any.whl", hash = "sha256:788ee0f3e036dca2dc20298d65e480297d348a44c9d7b2ee05c5262983e66072"},
-    {file = "mkdocs_material-9.5.11.tar.gz", hash = "sha256:7af7f8af0dea16175558f3fb9245d26c83a17199baa5f157755e63d7437bf971"},
+    {file = "mkdocs_material-9.5.13-py3-none-any.whl", hash = "sha256:5cbe17fee4e3b4980c8420a04cc762d8dc052ef1e10532abd4fce88e5ea9ce6a"},
+    {file = "mkdocs_material-9.5.13.tar.gz", hash = "sha256:d8e4caae576312a88fd2609b81cf43d233cdbe36860d67a68702b018b425bd87"},
 ]
 
 [package.dependencies]
@@ -1005,103 +1022,103 @@ files = [
 
 [[package]]
 name = "msgpack"
-version = "1.0.7"
+version = "1.0.8"
 description = "MessagePack serializer"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"},
-    {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"},
-    {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"},
-    {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"},
-    {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"},
-    {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"},
-    {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"},
-    {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"},
-    {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"},
-    {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"},
-    {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"},
-    {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"},
-    {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"},
-    {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"},
-    {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"},
-    {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"},
-    {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"},
-    {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"},
-    {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"},
-    {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"},
-    {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"},
-    {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"},
-    {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"},
-    {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"},
-    {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"},
-    {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"},
-    {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"},
-    {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"},
-    {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"},
-    {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"},
-    {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"},
-    {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"},
-    {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"},
-    {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"},
-    {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"},
-    {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"},
-    {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"},
-    {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"},
-    {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"},
-    {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"},
-    {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"},
-    {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"},
-    {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"},
-    {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"},
-    {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"},
-    {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"},
-    {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"},
-    {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"},
-    {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"},
-    {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"},
-    {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"},
-    {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"},
-    {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"},
-    {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"},
-    {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"},
-    {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"},
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"},
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"},
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"},
+    {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"},
+    {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"},
+    {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"},
+    {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"},
+    {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"},
+    {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"},
+    {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"},
+    {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
+    {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
+    {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
+    {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
 ]
 
 [[package]]
 name = "mypy"
-version = "1.8.0"
+version = "1.9.0"
 description = "Optional static typing for Python"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
-    {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
-    {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
-    {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
-    {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
-    {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
-    {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
-    {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
-    {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
-    {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
-    {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"},
-    {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"},
-    {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"},
-    {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"},
-    {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"},
-    {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"},
-    {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"},
-    {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"},
-    {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"},
-    {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"},
-    {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"},
-    {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"},
-    {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"},
-    {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"},
-    {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"},
-    {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
-    {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
+    {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
+    {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
+    {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
+    {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
+    {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
+    {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
+    {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
+    {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
+    {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
+    {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
+    {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
+    {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
+    {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
+    {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
+    {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
+    {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
+    {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
+    {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
+    {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
+    {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
+    {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
+    {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
+    {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
+    {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
+    {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
+    {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
+    {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
 ]
 
 [package.dependencies]
@@ -1142,13 +1159,13 @@ setuptools = "*"
 
 [[package]]
 name = "packaging"
-version = "23.2"
+version = "24.0"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
-    {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+    {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
+    {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
 ]
 
 [[package]]
@@ -1199,17 +1216,17 @@ ptyprocess = ">=0.5"
 
 [[package]]
 name = "pkginfo"
-version = "1.9.6"
+version = "1.10.0"
 description = "Query metadata from sdists / bdists / installed packages."
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"},
-    {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"},
+    {file = "pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"},
+    {file = "pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297"},
 ]
 
 [package.extras]
-testing = ["pytest", "pytest-cov"]
+testing = ["pytest", "pytest-cov", "wheel"]
 
 [[package]]
 name = "platformdirs"
@@ -1243,13 +1260,13 @@ testing = ["pytest", "pytest-benchmark"]
 
 [[package]]
 name = "poethepoet"
-version = "0.24.4"
+version = "0.25.0"
 description = "A task runner that works well with poetry."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"},
-    {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"},
+    {file = "poethepoet-0.25.0-py3-none-any.whl", hash = "sha256:42c0fd654f23e1b7c67aa8aa395c72e15eb275034bd5105171003daf679c1470"},
+    {file = "poethepoet-0.25.0.tar.gz", hash = "sha256:ca8f1d8475aa10d2ceeb26331d2626fc4a6b51df1e7e70d3d0d6481a984faab6"},
 ]
 
 [package.dependencies]
@@ -1261,13 +1278,13 @@ poetry-plugin = ["poetry (>=1.0,<2.0)"]
 
 [[package]]
 name = "poetry"
-version = "1.8.1"
+version = "1.8.2"
 description = "Python dependency management and packaging made easy."
 optional = false
 python-versions = ">=3.8,<4.0"
 files = [
-    {file = "poetry-1.8.1-py3-none-any.whl", hash = "sha256:cf133946661025822672422769567980f8e489ed5b6fc170d1025a4d6c9aac29"},
-    {file = "poetry-1.8.1.tar.gz", hash = "sha256:23519cc45eb3cf48e899145bc762425a141e3afd52ecc53ec443ca635122327f"},
+    {file = "poetry-1.8.2-py3-none-any.whl", hash = "sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910"},
+    {file = "poetry-1.8.2.tar.gz", hash = "sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73"},
 ]
 
 [package.dependencies]
@@ -1394,13 +1411,13 @@ windows-terminal = ["colorama (>=0.4.6)"]
 
 [[package]]
 name = "pymdown-extensions"
-version = "10.7"
+version = "10.7.1"
 description = "Extension pack for Python Markdown."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"},
-    {file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"},
+    {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"},
+    {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"},
 ]
 
 [package.dependencies]
@@ -1445,13 +1462,13 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
 
 [[package]]
 name = "pytest"
-version = "8.0.2"
+version = "8.1.1"
 description = "pytest: simple powerful testing with Python"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"},
-    {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"},
+    {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
+    {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
 ]
 
 [package.dependencies]
@@ -1459,11 +1476,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
 exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
 iniconfig = "*"
 packaging = "*"
-pluggy = ">=1.3.0,<2.0"
-tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+pluggy = ">=1.4,<2.0"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
 
 [package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
 
 [[package]]
 name = "pytest-cov"
@@ -1485,13 +1502,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
 
 [[package]]
 name = "python-dateutil"
-version = "2.8.2"
+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.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
-    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+    {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]
@@ -1584,101 +1601,101 @@ pyyaml = "*"
 
 [[package]]
 name = "rapidfuzz"
-version = "3.6.1"
+version = "3.6.2"
 description = "rapid fuzzy string matching"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f"},
-    {file = "rapidfuzz-3.6.1-cp310-cp310-win_arm64.whl", hash = "sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-win32.whl", hash = "sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe"},
-    {file = "rapidfuzz-3.6.1-cp311-cp311-win_arm64.whl", hash = "sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-win32.whl", hash = "sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1"},
-    {file = "rapidfuzz-3.6.1-cp312-cp312-win_arm64.whl", hash = "sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-win32.whl", hash = "sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105"},
-    {file = "rapidfuzz-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-win32.whl", hash = "sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198"},
-    {file = "rapidfuzz-3.6.1-cp39-cp39-win_arm64.whl", hash = "sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e"},
-    {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c"},
-    {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6"},
-    {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05"},
-    {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c"},
-    {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9"},
-    {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87"},
-    {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2"},
-    {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b"},
-    {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec"},
-    {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b"},
-    {file = "rapidfuzz-3.6.1.tar.gz", hash = "sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a5637e6bf11b15b5aff6ee818c76bdec99ad208511b78985e6209ba648a6e3ee"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:380586664f2f63807050ddb95e7702888b4f0b425abf17655940c411f39287ad"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3168ff565d4b8c239cf11fb604dd2507d30e9bcaac76a4077c0ac23cf2c866ed"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be69f7fd46b5c6467fe5e2fd4cff3816b0c03048eed8a4becb9a73e6000960e7"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbd5894f23fdf5697499cf759523639838ac822bd1600e343fdce7313baa02ae"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85a5b6e026393fe39fb61146b9c17c5af66fffbe1410e992c4bb06d9ec327bd3"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab269adfc64480f209e99f253391a10735edd5c09046e04899adab5fb132f20e"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35aeac852bca06023d6bbd50c1fc504ca5a9a3613d5e75a140f0be7601fa34ef"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e706f302c6a3ae0d74edd0d6ace46aee1ae07c563b436ccf5ff04db2b3571e60"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bec353f022011e6e5cd28ccb8700fbd2a33918197af0d4e0abb3c3f4845cc864"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ef3925daaa93eed20401012e219f569ff0c039ed5bf4ce2d3737b4f75d441622"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6ee98d88ae9ccc77ff61992ed33b2496478def5dc0da55c9a9aa06fcb725a352"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:423c7c588b09d618601097b7a0017dfcb91132a2076bef29023c5f3cd2dc3de1"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-win32.whl", hash = "sha256:c17c5efee347a40a6f4c1eec59e3d7d1e22f7613a97f8b8a07733ef723483a04"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:4209816626d8d6ff8ae7dc248061c6059e618b70c6e6f6e4d7444ae3740b2b85"},
+    {file = "rapidfuzz-3.6.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c54d3c85e522d3ac9ee39415f183c8fa184c4f87e7e5a37938f15a6d50e853a"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e06f6d270112f5db001f1cba5a97e1a48aee3d3dbdcbea3ec027c230462dbf9b"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:080cb71b50cb6aff11d1c6aeb157f273e2da0b2bdb3f9d7b01257e49e69a8576"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7895e04a22d6515bc91a850e0831f2405547605aa311d1ffec51e4818abc3c1"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82f9838519136b7083dd1e3149ee80344521f3dc37f744f227505ff0883efb"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a945567c2b0b6e069454c9782d5234b0b6795718adf7a9f868bd3144afa6a023"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:673ba2c343644805acdae1cb949c6a4de71aa2f62a998978551ebea59603af3f"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d457c89bac1471442002e70551e8268e639b3870b4a4521eae363c07253be87"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:495c0d8e14e6f12520eb7fc71b9ba9fcaafb47fc23a654e6e89b6c7985ec0020"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d67b649bf3e1b1722d04eca44d37919aef88305ce7ad05564502d013cf550fd"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e48dde8ca83d11daa00900cf6a5d281a1297aef9b7bfa73801af6e8822be5019"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:824cc381cf81cbf8d158f6935664ec2a69e6ac3b1d39fa201988bf81a257f775"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfe4c24957474ce0ac75d886387e30e292b4be39228a6d71f76de414dc187db"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d57b98013b802621bbc8b12a46bfc9d36ac552ab51ca207f7ce167ad46adabeb"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-win32.whl", hash = "sha256:9a07dffac439223b4f1025dbfc68f4445a3460a859309c9858c2a3fa29617cdc"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:95a49c6b8bf1229743ae585dd5b7d57f0d15a7eb6e826866d5c9965ba958503c"},
+    {file = "rapidfuzz-3.6.2-cp311-cp311-win_arm64.whl", hash = "sha256:af7c19ec86e11488539380d3db1755be5d561a3c0e7b04ff9d07abd7f9a8e9d8"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:de8adc12161bf282c60f12dc9233bb31632f71d446a010fe7469a69b8153427f"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:337e357f693130c4c6be740652542b260e36f622c59e01fa33d58f1d2750c930"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6468f8bc8c3c50604f43631550ef9cfec873515dba5023ca34d461be94669fc8"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74c6773b11445b5e5cf93ca383171cd0ac0cdeafea11a7b2a5688f8bf8d813e6"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1507fc5769aa109dda4de3a15f822a0f6a03e18d627bd0ba3ddbb253cf70e07"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:617949a70150e6fffdaed19253dd49f7a53528411dc8bf7663d499ba21e0f61e"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8b77779174b1b40aa70827692571ab457061897846255ad7d5d559e2edb1932"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80e51b22a7da83f9c87a97e92df07ed0612c74c35496590255f4b5d5b4212dfe"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3ae7c86914cb6673e97e187ba431b9c4cf4177d9ae77f8a1e5b2ba9a5628839e"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ddc380ffaa90f204cc9ddcb779114b9ab6f015246d549de9d47871a97ef9f18a"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c1dc078ef371fce09f9f3eec2ca4eaa2a8cd412ec53941015b4f39f14d34407"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a74102fc5a2534fe91f7507838623e1f3a149d8e05648389c42bb42e14b1c3f"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:48e1eaea8fcd522fca7f04f0480663f0f0cfb77957092cce60a93f4462864996"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-win32.whl", hash = "sha256:66b008bf2972740cd2dda5d382eb8bdb87265cd88198e71c7797bdc0d1f79d20"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:87ac3a87f2251ae2e95fc9478ca5c759de6d141d04c84d3fec9f9cdcfc167b33"},
+    {file = "rapidfuzz-3.6.2-cp312-cp312-win_arm64.whl", hash = "sha256:b593cc51aed887e93b78c2f94dfae9008be2b23d17afd3b1f1d3eb3913b58f26"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d830bc7a9b586a374147ec60b08b1f9ae5996b43f75cc514f37faef3866b519"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbee7f5ff11872b76505cbd87c814abc823e8757f11c69062eb3b25130a283da"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c011fb31f2c3f82f503aedd6097d3d3854e574e327a119a3b7eb2cf90b79ca"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cda81d0e0ce0c13abfa46b24e10c1e85f9c6acb628f0a9a948f5779f9c2076a2"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c279928651ce0e9e5220dcb25a00cc53b65e592a0861336a38299bcdca3a596"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35bd4bc9c40e6994c5d6edea4b9319388b4d9711c13c66d543bb4c37624b4184"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d07899506a5a8760448d9df036d528b55a554bf571714173635c79eef4a86e58"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb2e51d01b9c6d6954a3e055c57a80d4685b4fc82719db5519fc153566bcd6bb"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:153d065e353371cc0aeff32b99999a5758266a64e958d1364189367c1c9f6814"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4edcceebb85ebfa49a3ddcde20ad891d36c08dc0fd592efdab0e7d313a4e36af"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3549123fca5bb817341025f98e8e49ca99f84596c7c4f92b658f8e5836040d4a"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:84c1032ae42628465b7a5cc35249906061e18a8193c9c27cbd2db54e9823a9a6"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9bcc91ebd8fc69a6bd3b5711c8250f5f4e70606b4da75ef415f57ad209978205"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-win32.whl", hash = "sha256:f3a70f341c4c111bad910d2df69c78577a98af140319a996af24c9385939335d"},
+    {file = "rapidfuzz-3.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:354ad5fe655beb7b279390cb58334903931c5452ecbad1b1666ffb06786498e2"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1b86b93d93020c2b3edc1665d75c8855784845fc0a739b312c26c3a4bf0c80d5"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28243086ed0e50808bb56632e5442c457241646aeafafd501ac87901f40a3237"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed52461ae5a9ea4c400d38e2649c74a413f1a6d8fb8308b66f1fbd122514732f"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a46220f86a5f9cb016af31525e0d0865cad437d02239aa0d8aed2ab8bff1f1c"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81a630ed2fc3ec5fc7400eb66bab1f87e282b4d47f0abe3e48c6634dfa13b5e4"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8e5a437b9089df6242a718d9c31ab1742989e9400a0977af012ef483b63b4c2"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16270b5529de83b7bae7457e952e4d9cf3fbf029a837dd32d415bb9e0eb8e599"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5378c04102c7f084cde30a100154fa6d7e2baf0d51a6bdd2f912545559c1fb35"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f18397c8d6a65fc0b288d2fc29bc7baeea6ba91eeb95163a3cd98f23cd3bc85"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2acd2514defce81e6ff4bbff50252d5e7df8e85a731442c4b83e44c86cf1c916"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:1df2faf80201952e252413b6fac6f3e146080dcebb87bb1bb722508e67558ed8"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6440ed0b3007c1c9286b0b88fe2ab2d9e83edd60cd62293b3dfabb732b4e8a30"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4fcfa23b5553b27f4016df77c53172ea743454cf12c28cfa7c35a309a2be93b3"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-win32.whl", hash = "sha256:2d580d937146e803c8e5e1b87916cab8d6f84013b6392713e201efcda335c7d8"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:fe2a68be734e8e88af23385c68d6467e15818b6b1df1cbfebf7bff577226c957"},
+    {file = "rapidfuzz-3.6.2-cp39-cp39-win_arm64.whl", hash = "sha256:6478f7803efebf5f644d0b758439c5b25728550fdfbb19783d150004c46a75a9"},
+    {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36ce7b68a7b90b787cdd73480a68d2f1ca63c31a3a9d5a79a8736f978e1e9344"},
+    {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53597fd72a9340bcdd80d3620f4957c2b92f9b569313b969a3abdaffd193aae6"},
+    {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4f6de745fe6ce46a422d353ee10599013631d7d714a36d025f164b2d4e8c000"},
+    {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62df2136068e2515ed8beb01756381ff62c29384d785e3bf46e3111d4ea3ba1e"},
+    {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7382c90170f60c846c81a07ddd80bb2e8c43c8383754486fa37f67391a571897"},
+    {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f31314fd2e2f3dc3e519e6f93669462ce7953df2def1c344aa8f5345976d0eb2"},
+    {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012221629d54d3bee954148247f711eb86d4d390b589ebfe03172ea0b37a7531"},
+    {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d41dd59a70decfce6595315367a2fea2af660d92a9d144acc6479030501014d7"},
+    {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9fa14136a5b0cba1ec42531f7c3e0b0d3edb7fd6bc5e5ae7b498541f3855ab"},
+    {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:259364199cbfeca33b1af369fc7951f71717aa285184a3fa5a7b1772da1b89db"},
+    {file = "rapidfuzz-3.6.2.tar.gz", hash = "sha256:cf911e792ab0c431694c9bf2648afabfd92099103f2e31492893e078ddca5e1a"},
 ]
 
 [package.extras]
@@ -1941,18 +1958,18 @@ jeepney = ">=0.6"
 
 [[package]]
 name = "setuptools"
-version = "69.1.1"
+version = "69.2.0"
 description = "Easily download, build, install, upgrade, and uninstall Python packages"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"},
-    {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"},
+    {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"},
+    {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"},
 ]
 
 [package.extras]
 docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
 testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
 
 [[package]]
@@ -2001,13 +2018,13 @@ files = [
 
 [[package]]
 name = "tox"
-version = "4.13.0"
+version = "4.14.1"
 description = "tox is a generic virtualenv management and test command line tool"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "tox-4.13.0-py3-none-any.whl", hash = "sha256:1143c7e2489c68026a55d3d4ae84c02c449f073b28e62f80e3e440a3b72a4afa"},
-    {file = "tox-4.13.0.tar.gz", hash = "sha256:dd789a554c16c4b532924ba393c92fc8991323c4b3d466712bfecc8c9b9f24f7"},
+    {file = "tox-4.14.1-py3-none-any.whl", hash = "sha256:b03754b6ee6dadc70f2611da82b4ed8f625fcafd247e15d1d0cb056f90a06d3b"},
+    {file = "tox-4.14.1.tar.gz", hash = "sha256:f0ad758c3bbf7e237059c929d3595479363c3cdd5a06ac3e49d1dd020ffbee45"},
 ]
 
 [package.dependencies]
@@ -2028,13 +2045,13 @@ testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-po
 
 [[package]]
 name = "trove-classifiers"
-version = "2024.2.23"
+version = "2024.3.3"
 description = "Canonical source for classifiers on PyPI (pypi.org)."
 optional = false
 python-versions = "*"
 files = [
-    {file = "trove-classifiers-2024.2.23.tar.gz", hash = "sha256:8385160a12aac69c93fff058fb613472ed773a24a27eb3cd4b144cfbdd79f38c"},
-    {file = "trove_classifiers-2024.2.23-py3-none-any.whl", hash = "sha256:3094534b8021dc1822aadb1d11d4c7b62a854d464d19458fd0a49d6fe2b68b77"},
+    {file = "trove-classifiers-2024.3.3.tar.gz", hash = "sha256:df7edff9c67ff86b733628998330b180e81d125b1e096536d83ac0fd79673fdc"},
+    {file = "trove_classifiers-2024.3.3-py3-none-any.whl", hash = "sha256:3a84096861b385ec422c79995d1f6435dde47a9b63adaa3c886e53232ba7e6e0"},
 ]
 
 [[package]]
@@ -2126,6 +2143,85 @@ files = [
 [package.extras]
 watchmedo = ["PyYAML (>=3.10)"]
 
+[[package]]
+name = "wrapt"
+version = "1.16.0"
+description = "Module for decorators, wrappers and monkey patching."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
+    {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
+    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
+    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
+    {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
+    {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
+    {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
+    {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
+    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
+    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
+    {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
+    {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
+    {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
+    {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
+    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
+    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
+    {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
+    {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
+    {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
+    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
+    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
+    {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
+    {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
+    {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
+    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
+    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
+    {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
+    {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
+    {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
+    {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
+    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
+    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
+    {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
+    {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
+    {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
+    {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
+    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
+    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
+    {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
+    {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
+    {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
+    {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
+]
+
 [[package]]
 name = "xattr"
 version = "1.1.0"
@@ -2201,20 +2297,20 @@ test = ["pytest"]
 
 [[package]]
 name = "zipp"
-version = "3.17.0"
+version = "3.18.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
-    {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
+    {file = "zipp-3.18.0-py3-none-any.whl", hash = "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79"},
+    {file = "zipp-3.18.0.tar.gz", hash = "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"},
 ]
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
 
 [metadata]
 lock-version = "2.0"
 python-versions = ">=3.9 <4.0"
-content-hash = "57c3ade998bf42c29f1a2ee8db47a9cc4832e74733eacab96037b6558ed1d7e1"
+content-hash = "991b0d09ac6b4b84a88416a0f77b7bdb154cdac1c6a8aae37b1e28d18fc25471"
diff --git a/pyproject.toml b/pyproject.toml
index edfcbea..01d70c2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "Flunt"
-version = "2.1.1"
+version = "2.2.0"
 description = "Python implementation of Domain Notification Pattern inspired by Flunt (.NET)"
 authors = ["Emerson Delatorre <38289677+Delatorrea@users.noreply.github.com>"]
 repository = "https://github.com/Delatorrea/PyFlunt"
@@ -52,18 +52,18 @@ build-backend = "poetry.core.masonry.api"
 python = ">=3.9 <4.0"
 poetry = "^1.7.1"
 typing-extensions = "^4.9.0"
+deprecated = "^1.2.14"
 
 [tool.poetry.group.dev.dependencies]
 faker = "^23.1.0"
 pytest = "^8.0.1"
 pytest-cov = "^4.1.0"
 ruff = "^0.2.1"
-poethepoet = "^0.24.4"
 pre-commit = "^3.6.1"
 pre-commit-hooks = "^4.5.0"
 mypy = "^1.8.0"
 tox = "*"
-coverage = "^7.4.1"
+poethepoet = "^0.25.0"
 
 [tool.poetry.group.doc.dependencies]
 mkdocs-material = "^9.5.9"
@@ -106,20 +106,23 @@ ignore = [
 [tool.ruff.lint.pydocstyle]
 convention = "google"
 
+[tool.mypy]
+ignore_missing_imports = true
+
 [tool.poe]
 poetry_command = ""
 
 [tool.poe.tasks]
 test = "pytest"
 test-verbose = "pytest -vv"
-test-cov = "pytest --doctest-modules --cov=. --cov-report=html"
+test-cov = "pytest --cov=flunt --cov-report=html"
 tox = "tox -e py"
 pre-commit-install = "pre-commit install"
-pre-commit-run = "pre-commit run --all-files"
+pre-commit-run = "pre-commit run --all-files --show-diff-on-failure"
 lint = ["_ruff-check", "lint-check", "_ruff-format", "_mypy"]
 lint-check = "ruff check"
 export-dev = "poetry export -f requirements.txt --output requirements_dev.txt  --with dev"
 
 _ruff-check = "poetry run ruff check --select I --fix ."
 _ruff-format = "poetry run ruff format ."
-_mypy = "poetry run mypy ."
+_mypy = "poetry run mypy flunt tests"
diff --git a/requirements_dev.txt b/requirements/requirements_dev.txt
similarity index 68%
rename from requirements_dev.txt
rename to requirements/requirements_dev.txt
index 00f0676..d7d594a 100644
--- a/requirements_dev.txt
+++ b/requirements/requirements_dev.txt
@@ -1,6 +1,6 @@
-build==1.0.3 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b \
-    --hash=sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f
+build==1.1.1 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73 \
+    --hash=sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31
 cachecontrol[filecache]==0.14.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938 \
     --hash=sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0
@@ -166,59 +166,6 @@ cleo==2.1.0 ; python_version >= "3.9" and python_version < "4.0" \
 colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
-coverage==7.4.3 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa \
-    --hash=sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003 \
-    --hash=sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f \
-    --hash=sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c \
-    --hash=sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e \
-    --hash=sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0 \
-    --hash=sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9 \
-    --hash=sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52 \
-    --hash=sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e \
-    --hash=sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454 \
-    --hash=sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0 \
-    --hash=sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079 \
-    --hash=sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352 \
-    --hash=sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f \
-    --hash=sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30 \
-    --hash=sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe \
-    --hash=sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113 \
-    --hash=sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765 \
-    --hash=sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc \
-    --hash=sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e \
-    --hash=sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501 \
-    --hash=sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7 \
-    --hash=sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2 \
-    --hash=sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f \
-    --hash=sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4 \
-    --hash=sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524 \
-    --hash=sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c \
-    --hash=sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51 \
-    --hash=sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840 \
-    --hash=sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6 \
-    --hash=sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee \
-    --hash=sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e \
-    --hash=sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45 \
-    --hash=sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba \
-    --hash=sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d \
-    --hash=sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3 \
-    --hash=sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10 \
-    --hash=sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e \
-    --hash=sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb \
-    --hash=sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9 \
-    --hash=sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a \
-    --hash=sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47 \
-    --hash=sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1 \
-    --hash=sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3 \
-    --hash=sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914 \
-    --hash=sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328 \
-    --hash=sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6 \
-    --hash=sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d \
-    --hash=sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0 \
-    --hash=sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94 \
-    --hash=sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc \
-    --hash=sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2
 coverage[toml]==7.4.3 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa \
     --hash=sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003 \
@@ -308,6 +255,9 @@ cryptography==42.0.5 ; python_version >= "3.9" and python_version < "4.0" and sy
     --hash=sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e \
     --hash=sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac \
     --hash=sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7
+deprecated==1.2.14 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c \
+    --hash=sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3
 distlib==0.3.8 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \
     --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64
@@ -399,9 +349,9 @@ identify==2.5.35 ; python_version >= "3.9" and python_version < "4.0" \
 idna==3.6 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
     --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
-importlib-metadata==7.0.1 ; python_version >= "3.9" and python_version < "3.12" \
-    --hash=sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e \
-    --hash=sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc
+importlib-metadata==7.0.2 ; python_version >= "3.9" and python_version < "3.12" \
+    --hash=sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792 \
+    --hash=sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100
 iniconfig==2.0.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
     --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
@@ -420,127 +370,127 @@ keyring==24.3.1 ; python_version >= "3.9" and python_version < "4.0" \
 more-itertools==10.2.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684 \
     --hash=sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1
-msgpack==1.0.7 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862 \
-    --hash=sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d \
-    --hash=sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3 \
-    --hash=sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672 \
-    --hash=sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0 \
-    --hash=sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9 \
-    --hash=sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee \
-    --hash=sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46 \
-    --hash=sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524 \
-    --hash=sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819 \
-    --hash=sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc \
-    --hash=sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc \
-    --hash=sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1 \
-    --hash=sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82 \
-    --hash=sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81 \
-    --hash=sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6 \
-    --hash=sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d \
-    --hash=sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2 \
-    --hash=sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c \
-    --hash=sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87 \
-    --hash=sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84 \
-    --hash=sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e \
-    --hash=sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95 \
-    --hash=sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f \
-    --hash=sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b \
-    --hash=sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93 \
-    --hash=sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf \
-    --hash=sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61 \
-    --hash=sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c \
-    --hash=sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8 \
-    --hash=sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d \
-    --hash=sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c \
-    --hash=sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4 \
-    --hash=sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba \
-    --hash=sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415 \
-    --hash=sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee \
-    --hash=sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d \
-    --hash=sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9 \
-    --hash=sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075 \
-    --hash=sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f \
-    --hash=sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7 \
-    --hash=sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681 \
-    --hash=sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329 \
-    --hash=sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1 \
-    --hash=sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf \
-    --hash=sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c \
-    --hash=sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5 \
-    --hash=sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b \
-    --hash=sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5 \
-    --hash=sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e \
-    --hash=sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b \
-    --hash=sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad \
-    --hash=sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd \
-    --hash=sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7 \
-    --hash=sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002 \
-    --hash=sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc
+msgpack==1.0.8 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982 \
+    --hash=sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3 \
+    --hash=sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40 \
+    --hash=sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee \
+    --hash=sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693 \
+    --hash=sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950 \
+    --hash=sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151 \
+    --hash=sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24 \
+    --hash=sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca \
+    --hash=sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305 \
+    --hash=sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b \
+    --hash=sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c \
+    --hash=sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659 \
+    --hash=sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d \
+    --hash=sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18 \
+    --hash=sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746 \
+    --hash=sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868 \
+    --hash=sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2 \
+    --hash=sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba \
+    --hash=sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228 \
+    --hash=sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2 \
+    --hash=sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273 \
+    --hash=sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c \
+    --hash=sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653 \
+    --hash=sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a \
+    --hash=sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596 \
+    --hash=sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd \
+    --hash=sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8 \
+    --hash=sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa \
+    --hash=sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85 \
+    --hash=sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc \
+    --hash=sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836 \
+    --hash=sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58 \
+    --hash=sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128 \
+    --hash=sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db \
+    --hash=sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f \
+    --hash=sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77 \
+    --hash=sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad \
+    --hash=sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13 \
+    --hash=sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8 \
+    --hash=sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b \
+    --hash=sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a \
+    --hash=sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543 \
+    --hash=sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b \
+    --hash=sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce \
+    --hash=sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d \
+    --hash=sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a \
+    --hash=sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c \
+    --hash=sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f \
+    --hash=sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e \
+    --hash=sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011 \
+    --hash=sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04 \
+    --hash=sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480 \
+    --hash=sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a \
+    --hash=sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d \
+    --hash=sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d
 mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
     --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782
-mypy==1.8.0 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6 \
-    --hash=sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d \
-    --hash=sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02 \
-    --hash=sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d \
-    --hash=sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3 \
-    --hash=sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3 \
-    --hash=sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3 \
-    --hash=sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66 \
-    --hash=sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259 \
-    --hash=sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835 \
-    --hash=sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd \
-    --hash=sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d \
-    --hash=sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8 \
-    --hash=sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07 \
-    --hash=sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b \
-    --hash=sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e \
-    --hash=sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6 \
-    --hash=sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae \
-    --hash=sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9 \
-    --hash=sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d \
-    --hash=sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a \
-    --hash=sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592 \
-    --hash=sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218 \
-    --hash=sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817 \
-    --hash=sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4 \
-    --hash=sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410 \
-    --hash=sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55
+mypy==1.9.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6 \
+    --hash=sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913 \
+    --hash=sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129 \
+    --hash=sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc \
+    --hash=sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974 \
+    --hash=sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374 \
+    --hash=sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150 \
+    --hash=sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03 \
+    --hash=sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9 \
+    --hash=sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02 \
+    --hash=sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89 \
+    --hash=sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2 \
+    --hash=sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d \
+    --hash=sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3 \
+    --hash=sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612 \
+    --hash=sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e \
+    --hash=sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3 \
+    --hash=sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e \
+    --hash=sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd \
+    --hash=sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04 \
+    --hash=sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed \
+    --hash=sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185 \
+    --hash=sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf \
+    --hash=sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b \
+    --hash=sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4 \
+    --hash=sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f \
+    --hash=sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6
 nodeenv==1.8.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2 \
     --hash=sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec
-packaging==23.2 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
-    --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
+packaging==24.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
+    --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9
 pastel==0.2.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364 \
     --hash=sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d
 pexpect==4.9.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \
     --hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f
-pkginfo==1.9.6 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \
-    --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046
+pkginfo==1.10.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \
+    --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097
 platformdirs==4.2.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068 \
     --hash=sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768
 pluggy==1.4.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981 \
     --hash=sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be
-poethepoet==0.24.4 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075 \
-    --hash=sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef
+poethepoet==0.25.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:42c0fd654f23e1b7c67aa8aa395c72e15eb275034bd5105171003daf679c1470 \
+    --hash=sha256:ca8f1d8475aa10d2ceeb26331d2626fc4a6b51df1e7e70d3d0d6481a984faab6
 poetry-core==1.9.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:4e0c9c6ad8cf89956f03b308736d84ea6ddb44089d16f2adc94050108ec1f5a1 \
     --hash=sha256:fa7a4001eae8aa572ee84f35feb510b321bd652e5cf9293249d62853e1f935a2
 poetry-plugin-export==1.6.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:091939434984267a91abf2f916a26b00cff4eee8da63ec2a24ba4b17cf969a59 \
     --hash=sha256:2dce6204c9318f1f6509a11a03921fb3f461b201840b59f1c237b6ab454dabcf
-poetry==1.8.1 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:23519cc45eb3cf48e899145bc762425a141e3afd52ecc53ec443ca635122327f \
-    --hash=sha256:cf133946661025822672422769567980f8e489ed5b6fc170d1025a4d6c9aac29
+poetry==1.8.2 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73 \
+    --hash=sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910
 pre-commit-hooks==4.5.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:b779d5c44ede9b1fda48e2d96b08e9aa5b1d2fdb8903ca09f0dbaca22d529edb \
     --hash=sha256:ffbe2af1c85ac9a7695866955680b4dee98822638b748a6f3debefad79748c8a
@@ -562,12 +512,12 @@ pyproject-hooks==1.0.0 ; python_version >= "3.9" and python_version < "4.0" \
 pytest-cov==4.1.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6 \
     --hash=sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a
-pytest==8.0.2 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd \
-    --hash=sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096
-python-dateutil==2.8.2 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
-    --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
+pytest==8.1.1 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7 \
+    --hash=sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044
+python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
+    --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
 pywin32-ctypes==0.2.2 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "win32" \
     --hash=sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60 \
     --hash=sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7
@@ -623,97 +573,97 @@ pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
     --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
     --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
-rapidfuzz==3.6.1 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc \
-    --hash=sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73 \
-    --hash=sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d \
-    --hash=sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab \
-    --hash=sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11 \
-    --hash=sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15 \
-    --hash=sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3 \
-    --hash=sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526 \
-    --hash=sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66 \
-    --hash=sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89 \
-    --hash=sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d \
-    --hash=sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0 \
-    --hash=sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e \
-    --hash=sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a \
-    --hash=sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0 \
-    --hash=sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0 \
-    --hash=sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848 \
-    --hash=sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5 \
-    --hash=sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0 \
-    --hash=sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec \
-    --hash=sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967 \
-    --hash=sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382 \
-    --hash=sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b \
-    --hash=sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7 \
-    --hash=sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9 \
-    --hash=sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b \
-    --hash=sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764 \
-    --hash=sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f \
-    --hash=sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52 \
-    --hash=sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261 \
-    --hash=sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b \
-    --hash=sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9 \
-    --hash=sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6 \
-    --hash=sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720 \
-    --hash=sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa \
-    --hash=sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74 \
-    --hash=sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c \
-    --hash=sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2 \
-    --hash=sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786 \
-    --hash=sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05 \
-    --hash=sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e \
-    --hash=sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8 \
-    --hash=sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0 \
-    --hash=sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791 \
-    --hash=sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b \
-    --hash=sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105 \
-    --hash=sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460 \
-    --hash=sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9 \
-    --hash=sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca \
-    --hash=sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c \
-    --hash=sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f \
-    --hash=sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9 \
-    --hash=sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283 \
-    --hash=sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854 \
-    --hash=sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab \
-    --hash=sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24 \
-    --hash=sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6 \
-    --hash=sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40 \
-    --hash=sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1 \
-    --hash=sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d \
-    --hash=sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1 \
-    --hash=sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb \
-    --hash=sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3 \
-    --hash=sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928 \
-    --hash=sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87 \
-    --hash=sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e \
-    --hash=sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41 \
-    --hash=sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379 \
-    --hash=sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b \
-    --hash=sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9 \
-    --hash=sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759 \
-    --hash=sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607 \
-    --hash=sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5 \
-    --hash=sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b \
-    --hash=sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe \
-    --hash=sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222 \
-    --hash=sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1 \
-    --hash=sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6 \
-    --hash=sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90 \
-    --hash=sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f \
-    --hash=sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06 \
-    --hash=sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198 \
-    --hash=sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0 \
-    --hash=sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891 \
-    --hash=sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c \
-    --hash=sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799 \
-    --hash=sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c \
-    --hash=sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802 \
-    --hash=sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6 \
-    --hash=sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d
+rapidfuzz==3.6.2 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:012221629d54d3bee954148247f711eb86d4d390b589ebfe03172ea0b37a7531 \
+    --hash=sha256:080cb71b50cb6aff11d1c6aeb157f273e2da0b2bdb3f9d7b01257e49e69a8576 \
+    --hash=sha256:153d065e353371cc0aeff32b99999a5758266a64e958d1364189367c1c9f6814 \
+    --hash=sha256:16270b5529de83b7bae7457e952e4d9cf3fbf029a837dd32d415bb9e0eb8e599 \
+    --hash=sha256:1b86b93d93020c2b3edc1665d75c8855784845fc0a739b312c26c3a4bf0c80d5 \
+    --hash=sha256:1c54d3c85e522d3ac9ee39415f183c8fa184c4f87e7e5a37938f15a6d50e853a \
+    --hash=sha256:1df2faf80201952e252413b6fac6f3e146080dcebb87bb1bb722508e67558ed8 \
+    --hash=sha256:1dfe4c24957474ce0ac75d886387e30e292b4be39228a6d71f76de414dc187db \
+    --hash=sha256:259364199cbfeca33b1af369fc7951f71717aa285184a3fa5a7b1772da1b89db \
+    --hash=sha256:28243086ed0e50808bb56632e5442c457241646aeafafd501ac87901f40a3237 \
+    --hash=sha256:28c011fb31f2c3f82f503aedd6097d3d3854e574e327a119a3b7eb2cf90b79ca \
+    --hash=sha256:2a46220f86a5f9cb016af31525e0d0865cad437d02239aa0d8aed2ab8bff1f1c \
+    --hash=sha256:2acd2514defce81e6ff4bbff50252d5e7df8e85a731442c4b83e44c86cf1c916 \
+    --hash=sha256:2d580d937146e803c8e5e1b87916cab8d6f84013b6392713e201efcda335c7d8 \
+    --hash=sha256:3168ff565d4b8c239cf11fb604dd2507d30e9bcaac76a4077c0ac23cf2c866ed \
+    --hash=sha256:337e357f693130c4c6be740652542b260e36f622c59e01fa33d58f1d2750c930 \
+    --hash=sha256:3549123fca5bb817341025f98e8e49ca99f84596c7c4f92b658f8e5836040d4a \
+    --hash=sha256:354ad5fe655beb7b279390cb58334903931c5452ecbad1b1666ffb06786498e2 \
+    --hash=sha256:35aeac852bca06023d6bbd50c1fc504ca5a9a3613d5e75a140f0be7601fa34ef \
+    --hash=sha256:35bd4bc9c40e6994c5d6edea4b9319388b4d9711c13c66d543bb4c37624b4184 \
+    --hash=sha256:36ce7b68a7b90b787cdd73480a68d2f1ca63c31a3a9d5a79a8736f978e1e9344 \
+    --hash=sha256:380586664f2f63807050ddb95e7702888b4f0b425abf17655940c411f39287ad \
+    --hash=sha256:3ae7c86914cb6673e97e187ba431b9c4cf4177d9ae77f8a1e5b2ba9a5628839e \
+    --hash=sha256:3c1dc078ef371fce09f9f3eec2ca4eaa2a8cd412ec53941015b4f39f14d34407 \
+    --hash=sha256:4209816626d8d6ff8ae7dc248061c6059e618b70c6e6f6e4d7444ae3740b2b85 \
+    --hash=sha256:423c7c588b09d618601097b7a0017dfcb91132a2076bef29023c5f3cd2dc3de1 \
+    --hash=sha256:48e1eaea8fcd522fca7f04f0480663f0f0cfb77957092cce60a93f4462864996 \
+    --hash=sha256:495c0d8e14e6f12520eb7fc71b9ba9fcaafb47fc23a654e6e89b6c7985ec0020 \
+    --hash=sha256:4c279928651ce0e9e5220dcb25a00cc53b65e592a0861336a38299bcdca3a596 \
+    --hash=sha256:4edcceebb85ebfa49a3ddcde20ad891d36c08dc0fd592efdab0e7d313a4e36af \
+    --hash=sha256:4fcfa23b5553b27f4016df77c53172ea743454cf12c28cfa7c35a309a2be93b3 \
+    --hash=sha256:53597fd72a9340bcdd80d3620f4957c2b92f9b569313b969a3abdaffd193aae6 \
+    --hash=sha256:5378c04102c7f084cde30a100154fa6d7e2baf0d51a6bdd2f912545559c1fb35 \
+    --hash=sha256:617949a70150e6fffdaed19253dd49f7a53528411dc8bf7663d499ba21e0f61e \
+    --hash=sha256:62df2136068e2515ed8beb01756381ff62c29384d785e3bf46e3111d4ea3ba1e \
+    --hash=sha256:6440ed0b3007c1c9286b0b88fe2ab2d9e83edd60cd62293b3dfabb732b4e8a30 \
+    --hash=sha256:6468f8bc8c3c50604f43631550ef9cfec873515dba5023ca34d461be94669fc8 \
+    --hash=sha256:6478f7803efebf5f644d0b758439c5b25728550fdfbb19783d150004c46a75a9 \
+    --hash=sha256:66b008bf2972740cd2dda5d382eb8bdb87265cd88198e71c7797bdc0d1f79d20 \
+    --hash=sha256:673ba2c343644805acdae1cb949c6a4de71aa2f62a998978551ebea59603af3f \
+    --hash=sha256:6d67b649bf3e1b1722d04eca44d37919aef88305ce7ad05564502d013cf550fd \
+    --hash=sha256:6ee98d88ae9ccc77ff61992ed33b2496478def5dc0da55c9a9aa06fcb725a352 \
+    --hash=sha256:7382c90170f60c846c81a07ddd80bb2e8c43c8383754486fa37f67391a571897 \
+    --hash=sha256:74c6773b11445b5e5cf93ca383171cd0ac0cdeafea11a7b2a5688f8bf8d813e6 \
+    --hash=sha256:7d830bc7a9b586a374147ec60b08b1f9ae5996b43f75cc514f37faef3866b519 \
+    --hash=sha256:7f18397c8d6a65fc0b288d2fc29bc7baeea6ba91eeb95163a3cd98f23cd3bc85 \
+    --hash=sha256:7f9fa14136a5b0cba1ec42531f7c3e0b0d3edb7fd6bc5e5ae7b498541f3855ab \
+    --hash=sha256:80e51b22a7da83f9c87a97e92df07ed0612c74c35496590255f4b5d5b4212dfe \
+    --hash=sha256:81a630ed2fc3ec5fc7400eb66bab1f87e282b4d47f0abe3e48c6634dfa13b5e4 \
+    --hash=sha256:824cc381cf81cbf8d158f6935664ec2a69e6ac3b1d39fa201988bf81a257f775 \
+    --hash=sha256:84c1032ae42628465b7a5cc35249906061e18a8193c9c27cbd2db54e9823a9a6 \
+    --hash=sha256:85a5b6e026393fe39fb61146b9c17c5af66fffbe1410e992c4bb06d9ec327bd3 \
+    --hash=sha256:87ac3a87f2251ae2e95fc9478ca5c759de6d141d04c84d3fec9f9cdcfc167b33 \
+    --hash=sha256:95a49c6b8bf1229743ae585dd5b7d57f0d15a7eb6e826866d5c9965ba958503c \
+    --hash=sha256:9a07dffac439223b4f1025dbfc68f4445a3460a859309c9858c2a3fa29617cdc \
+    --hash=sha256:9a74102fc5a2534fe91f7507838623e1f3a149d8e05648389c42bb42e14b1c3f \
+    --hash=sha256:9bcc91ebd8fc69a6bd3b5711c8250f5f4e70606b4da75ef415f57ad209978205 \
+    --hash=sha256:9d457c89bac1471442002e70551e8268e639b3870b4a4521eae363c07253be87 \
+    --hash=sha256:a5637e6bf11b15b5aff6ee818c76bdec99ad208511b78985e6209ba648a6e3ee \
+    --hash=sha256:a7895e04a22d6515bc91a850e0831f2405547605aa311d1ffec51e4818abc3c1 \
+    --hash=sha256:a945567c2b0b6e069454c9782d5234b0b6795718adf7a9f868bd3144afa6a023 \
+    --hash=sha256:ab269adfc64480f209e99f253391a10735edd5c09046e04899adab5fb132f20e \
+    --hash=sha256:af7c19ec86e11488539380d3db1755be5d561a3c0e7b04ff9d07abd7f9a8e9d8 \
+    --hash=sha256:b593cc51aed887e93b78c2f94dfae9008be2b23d17afd3b1f1d3eb3913b58f26 \
+    --hash=sha256:bd82f9838519136b7083dd1e3149ee80344521f3dc37f744f227505ff0883efb \
+    --hash=sha256:be69f7fd46b5c6467fe5e2fd4cff3816b0c03048eed8a4becb9a73e6000960e7 \
+    --hash=sha256:bec353f022011e6e5cd28ccb8700fbd2a33918197af0d4e0abb3c3f4845cc864 \
+    --hash=sha256:c17c5efee347a40a6f4c1eec59e3d7d1e22f7613a97f8b8a07733ef723483a04 \
+    --hash=sha256:cbd5894f23fdf5697499cf759523639838ac822bd1600e343fdce7313baa02ae \
+    --hash=sha256:cda81d0e0ce0c13abfa46b24e10c1e85f9c6acb628f0a9a948f5779f9c2076a2 \
+    --hash=sha256:cf911e792ab0c431694c9bf2648afabfd92099103f2e31492893e078ddca5e1a \
+    --hash=sha256:d07899506a5a8760448d9df036d528b55a554bf571714173635c79eef4a86e58 \
+    --hash=sha256:d41dd59a70decfce6595315367a2fea2af660d92a9d144acc6479030501014d7 \
+    --hash=sha256:d4f6de745fe6ce46a422d353ee10599013631d7d714a36d025f164b2d4e8c000 \
+    --hash=sha256:d57b98013b802621bbc8b12a46bfc9d36ac552ab51ca207f7ce167ad46adabeb \
+    --hash=sha256:d8e5a437b9089df6242a718d9c31ab1742989e9400a0977af012ef483b63b4c2 \
+    --hash=sha256:dbee7f5ff11872b76505cbd87c814abc823e8757f11c69062eb3b25130a283da \
+    --hash=sha256:ddc380ffaa90f204cc9ddcb779114b9ab6f015246d549de9d47871a97ef9f18a \
+    --hash=sha256:de8adc12161bf282c60f12dc9233bb31632f71d446a010fe7469a69b8153427f \
+    --hash=sha256:e06f6d270112f5db001f1cba5a97e1a48aee3d3dbdcbea3ec027c230462dbf9b \
+    --hash=sha256:e1507fc5769aa109dda4de3a15f822a0f6a03e18d627bd0ba3ddbb253cf70e07 \
+    --hash=sha256:e48dde8ca83d11daa00900cf6a5d281a1297aef9b7bfa73801af6e8822be5019 \
+    --hash=sha256:e706f302c6a3ae0d74edd0d6ace46aee1ae07c563b436ccf5ff04db2b3571e60 \
+    --hash=sha256:eb2e51d01b9c6d6954a3e055c57a80d4685b4fc82719db5519fc153566bcd6bb \
+    --hash=sha256:ed52461ae5a9ea4c400d38e2649c74a413f1a6d8fb8308b66f1fbd122514732f \
+    --hash=sha256:ef3925daaa93eed20401012e219f569ff0c039ed5bf4ce2d3737b4f75d441622 \
+    --hash=sha256:f31314fd2e2f3dc3e519e6f93669462ce7953df2def1c344aa8f5345976d0eb2 \
+    --hash=sha256:f3a70f341c4c111bad910d2df69c78577a98af140319a996af24c9385939335d \
+    --hash=sha256:f8b77779174b1b40aa70827692571ab457061897846255ad7d5d559e2edb1932 \
+    --hash=sha256:fe2a68be734e8e88af23385c68d6467e15818b6b1df1cbfebf7bff577226c957
 requests-toolbelt==1.0.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6 \
     --hash=sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06
@@ -795,9 +745,9 @@ ruff==0.2.2 ; python_version >= "3.9" and python_version < "4.0" \
 secretstorage==3.3.3 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "linux" \
     --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \
     --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99
-setuptools==69.1.1 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56 \
-    --hash=sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8
+setuptools==69.2.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \
+    --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c
 shellingham==1.5.4 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \
     --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de
@@ -810,12 +760,12 @@ tomli==2.0.1 ; python_version >= "3.9" and python_version < "4.0" \
 tomlkit==0.12.4 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b \
     --hash=sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3
-tox==4.13.0 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:1143c7e2489c68026a55d3d4ae84c02c449f073b28e62f80e3e440a3b72a4afa \
-    --hash=sha256:dd789a554c16c4b532924ba393c92fc8991323c4b3d466712bfecc8c9b9f24f7
-trove-classifiers==2024.2.23 ; python_version >= "3.9" and python_version < "4.0" \
-    --hash=sha256:3094534b8021dc1822aadb1d11d4c7b62a854d464d19458fd0a49d6fe2b68b77 \
-    --hash=sha256:8385160a12aac69c93fff058fb613472ed773a24a27eb3cd4b144cfbdd79f38c
+tox==4.14.1 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:b03754b6ee6dadc70f2611da82b4ed8f625fcafd247e15d1d0cb056f90a06d3b \
+    --hash=sha256:f0ad758c3bbf7e237059c929d3595479363c3cdd5a06ac3e49d1dd020ffbee45
+trove-classifiers==2024.3.3 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:3a84096861b385ec422c79995d1f6435dde47a9b63adaa3c886e53232ba7e6e0 \
+    --hash=sha256:df7edff9c67ff86b733628998330b180e81d125b1e096536d83ac0fd79673fdc
 typing-extensions==4.10.0 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \
     --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb
@@ -825,6 +775,77 @@ urllib3==2.2.1 ; python_version >= "3.9" and python_version < "4.0" \
 virtualenv==20.25.1 ; python_version >= "3.9" and python_version < "4.0" \
     --hash=sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a \
     --hash=sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197
+wrapt==1.16.0 ; python_version >= "3.9" and python_version < "4.0" \
+    --hash=sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc \
+    --hash=sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81 \
+    --hash=sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09 \
+    --hash=sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e \
+    --hash=sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca \
+    --hash=sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0 \
+    --hash=sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb \
+    --hash=sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487 \
+    --hash=sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40 \
+    --hash=sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c \
+    --hash=sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060 \
+    --hash=sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202 \
+    --hash=sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41 \
+    --hash=sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9 \
+    --hash=sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b \
+    --hash=sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664 \
+    --hash=sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d \
+    --hash=sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362 \
+    --hash=sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00 \
+    --hash=sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc \
+    --hash=sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1 \
+    --hash=sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267 \
+    --hash=sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956 \
+    --hash=sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966 \
+    --hash=sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1 \
+    --hash=sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228 \
+    --hash=sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72 \
+    --hash=sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d \
+    --hash=sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292 \
+    --hash=sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0 \
+    --hash=sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0 \
+    --hash=sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36 \
+    --hash=sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c \
+    --hash=sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5 \
+    --hash=sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f \
+    --hash=sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73 \
+    --hash=sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b \
+    --hash=sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2 \
+    --hash=sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593 \
+    --hash=sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39 \
+    --hash=sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389 \
+    --hash=sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf \
+    --hash=sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf \
+    --hash=sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89 \
+    --hash=sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c \
+    --hash=sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c \
+    --hash=sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f \
+    --hash=sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440 \
+    --hash=sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465 \
+    --hash=sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136 \
+    --hash=sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b \
+    --hash=sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8 \
+    --hash=sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3 \
+    --hash=sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8 \
+    --hash=sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6 \
+    --hash=sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e \
+    --hash=sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f \
+    --hash=sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c \
+    --hash=sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e \
+    --hash=sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8 \
+    --hash=sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2 \
+    --hash=sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020 \
+    --hash=sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35 \
+    --hash=sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d \
+    --hash=sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3 \
+    --hash=sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537 \
+    --hash=sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809 \
+    --hash=sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d \
+    --hash=sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a \
+    --hash=sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4
 xattr==1.1.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "darwin" \
     --hash=sha256:00d2b415cf9d6a24112d019e721aa2a85652f7bbc9f3b9574b2d1cd8668eb491 \
     --hash=sha256:0683dae7609f7280b0c89774d00b5957e6ffcb181c6019c46632b389706b77e6 \
@@ -884,6 +905,6 @@ xattr==1.1.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platfo
     --hash=sha256:fa6a7af7a4ada43f15ccc58b6f9adcdbff4c36ba040013d2681e589e07ae280a \
     --hash=sha256:fecbf3b05043ed3487a28190dec3e4c4d879b2fcec0e30bafd8ec5d4b6043630 \
     --hash=sha256:ff6223a854229055e803c2ad0c0ea9a6da50c6be30d92c198cf5f9f28819a921
-zipp==3.17.0 ; python_version >= "3.9" and python_version < "3.12" \
-    --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
-    --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0
+zipp==3.18.0 ; python_version >= "3.9" and python_version < "3.12" \
+    --hash=sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79 \
+    --hash=sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f
diff --git a/tox.ini b/tox.ini
index d7c465d..109dff2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,30 +1,28 @@
 [tox]
-envlist = py39, py310, py311, py312, ruff, mypy
-isolated_build = true
+envlist =
+    lint,
+    py{39,310,311,312},
 
 [gh-actions]
 python =
     3.9: py39
     3.10: py310
     3.11: py311
-    3.12: py312, mypy, ruff
+    3.12: py312
 
 [testenv]
 setenv =
     PYTHONPATH = {toxinidir}
 deps =
-    -r{toxinidir}/requirements_dev.txt
+    -r{toxinidir}/requirements/requirements_dev.txt
 commands =
-    coverage run -m pytest
-    coverage xml
+    pytest --cov=flunt --cov-report=xml
 
-[testenv:ruff]
-basepython = python3.12
-deps = ruff
-commands = ruff check
-
-[testenv:mypy]
-basepython = python3.12
+[testenv:lint]
+basepython = python3.9
 deps =
-    -r{toxinidir}/requirements_dev.txt
-commands = mypy .
+    pre-commit
+    mypy
+commands =
+    pre-commit run --all-files --show-diff-on-failure
+    mypy flunt tests


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c220aae..b57ace3 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -11,7 +11,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        python-version: ["3.10", "3.11", "3.12"]
+        python-version: ["3.9", "3.10", "3.11", "3.12"]
     steps:
       - name: Realiza o checkout
         uses: actions/checkout@v4
diff --git a/tests/test_notifiable.py b/tests/test_notifiable.py
index 66c274d..c43a7c4 100644
--- a/tests/test_notifiable.py
+++ b/tests/test_notifiable.py
@@ -3,8 +3,6 @@
 
 def test_quando_nome_possuir_mais_que_2_e_menos_que_51_caracteres_deve_ser_valido():
 	nome = Name('Emerson', 'Delatorre')
-	for notificacao in nome.get_notifications():
-		print(notificacao.message)
 	assert nome.is_valid
 
 
diff --git a/tests/validations/test_bool_validation_contract.py b/tests/validations/test_bool_validation_contract.py
index a68fedd..0f9f6c9 100644
--- a/tests/validations/test_bool_validation_contract.py
+++ b/tests/validations/test_bool_validation_contract.py
@@ -1,79 +1,41 @@
-from flunt.validations.contract import Contract
+import pytest
 
-message = 'Custom message here'
+from flunt.validations.bool_validation_contract import BoolValidationContract
 
+@pytest.fixture()
+def message():
+    yield 'Custom message here'
 
-def test_should_be_valid_when_is_true(entity_mock):
+
+def test_should_be_valid_when_is_true(entity_mock, message):
 	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
+		BoolValidationContract()
 		.is_true(entity_mock.bool_true_property, 'Bool', message)
 	)
 	assert contract.is_valid
 
 
-def test_should_be_valid_when_is_false(entity_mock):
+def test_should_be_valid_when_is_false(entity_mock, message):
 	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
+		BoolValidationContract()
 		.is_false(entity_mock.bool_false_property, 'Bool', message)
 	)
 	assert contract.is_valid
 
 
-def test_should_return_a_once_notification_when_is_true_is_invalid(entity_mock):
+def test_should_return_a_once_notification_when_is_true_is_invalid(entity_mock, message):
 	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
+		BoolValidationContract()
 		.is_true(entity_mock.bool_false_property, 'Bool', message)
 	)
 	assert len(contract.get_notifications()) == 1
 
 
 def test_should_return_a_once_notification_when_is_false_is_invalid(
-	entity_mock,
+	entity_mock, message
 ):
 	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
+		BoolValidationContract()
 		.is_false(entity_mock.bool_true_property, 'Bool', message)
 	)
 	assert len(contract.get_notifications()) == 1
-
-
-def test_should_return_valid_when_is_none_is_valid(entity_mock):
-	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
-		.is_none(entity_mock.bool_none_property, 'Bool', message)
-	)
-	assert contract.is_valid
-
-
-def test_should_return_a_once_notification_when_is_none_is_invalid(entity_mock):
-	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
-		.is_none(entity_mock.bool_true_property, 'Bool', message)
-	)
-	assert len(contract.get_notifications()) == 1
-
-
-def test_should_return_valid_when_is_not_none_is_valid(entity_mock):
-	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
-		.is_not_none(entity_mock.bool_true_property, 'Bool', message)
-	)
-	assert contract.is_valid
-
-
-def test_should_return_a_once_notification_when_is_not_none_is_invalid(
-	entity_mock,
-):
-	contract = (
-		Contract()
-		.requires(entity_mock.bool_true_property, 'Bool', message)
-		.is_not_none(entity_mock.bool_none_property, 'Bool', message)
-	)
-	assert len(contract.get_notifications()) == 1
diff --git a/tests/validations/test_commons_validation_contract.py b/tests/validations/test_commons_validation_contract.py
new file mode 100644
index 0000000..25a2898
--- /dev/null
+++ b/tests/validations/test_commons_validation_contract.py
@@ -0,0 +1,143 @@
+import pytest
+from faker import Faker
+from uuid import uuid4
+from flunt.validations.commons_validation_contract import CommonsValidationContract
+
+
+fake = Faker()
+
+@pytest.mark.parametrize(
+	'input',
+	[
+		fake.uuid4(),
+		fake.date_object(),
+        fake.json(),
+        fake.pydecimal(),
+        fake.pydict(),
+        fake.pyfloat(),
+        fake.pyint(),
+        fake.pyiterable(),
+        fake.pylist(),
+        fake.pyset(),
+        fake.pystr(),
+        fake.pystr_format(),
+        fake.pystruct(),
+        fake.pytuple(),
+        fake.pybool(),
+        uuid4()
+	],
+)
+def test_should_return_true_when_are_equals_receives_two_values_are_equal_regardless_of_the_type(input):
+    contract =  CommonsValidationContract()
+    contract.are_equals(input, input, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid
+
+@pytest.mark.parametrize(
+	'input,expected',
+	[
+		(fake.uuid4(), fake.uuid4()),
+		(fake.date_object(), fake.date_object()),
+        (fake.json(), fake.json()),
+        (fake.pydecimal(), fake.pydecimal()),
+        (fake.pydict(), fake.pydict()),
+        (fake.pyfloat(), fake.pyfloat()),
+        (fake.pyint(), fake.pyint()),
+        (fake.pyiterable(), fake.pyiterable()),
+        (fake.pylist(), fake.pylist()),
+        (fake.pyset(), fake.pyset()),
+        (fake.pystr(), fake.pystr()),
+        (fake.pystr_format(), fake.pystr_format()),
+        (fake.pystruct(), fake.pystruct()),
+        (fake.pytuple(), fake.pytuple()),
+        (fake.pybool(100), fake.pybool(0)),
+        (fake.uuid4(), fake.date_object()),
+        (fake.json(), fake.pydecimal()),
+        (fake.pydict(), uuid4()),
+	],
+)
+def test_should_return_false_when_are_equals_receives_two_values_are_not_equal_regardless_of_the_type(input, expected):
+    contract =  CommonsValidationContract()
+    contract.are_equals(input, expected, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid is False
+
+@pytest.mark.parametrize(
+	'input',
+	[
+		fake.uuid4(),
+		fake.date_object(),
+        fake.json(),
+        fake.pydecimal(),
+        fake.pydict(),
+        fake.pyfloat(),
+        fake.pyint(),
+        fake.pyiterable(),
+        fake.pylist(),
+        fake.pyset(),
+        fake.pystr(),
+        fake.pystr_format(),
+        fake.pystruct(),
+        fake.pytuple(),
+        fake.pybool(),
+        uuid4()
+	],
+)
+def test_should_return_true_when_are_not_equals_receives_two_values_are_equal_regardless_of_the_type(input):
+    contract =  CommonsValidationContract()
+    contract.are_not_equals(input, input, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid is False
+
+@pytest.mark.parametrize(
+	'input,expected',
+	[
+		(fake.uuid4(), fake.uuid4()),
+		(fake.date_object(), fake.date_object()),
+        (fake.json(), fake.json()),
+        (fake.pydecimal(), fake.pydecimal()),
+        (fake.pydict(), fake.pydict()),
+        (fake.pyfloat(), fake.pyfloat()),
+        (fake.pyint(), fake.pyint()),
+        (fake.pyiterable(), fake.pyiterable()),
+        (fake.pylist(), fake.pylist()),
+        (fake.pyset(), fake.pyset()),
+        (fake.pystr(), fake.pystr()),
+        (fake.pystr_format(), fake.pystr_format()),
+        (fake.pystruct(), fake.pystruct()),
+        (fake.pytuple(), fake.pytuple()),
+        (fake.pybool(100), fake.pybool(0)),
+        (fake.uuid4(), fake.date_object()),
+        (fake.json(), fake.pydecimal()),
+        (fake.pydict(), uuid4()),
+	],
+)
+def test_should_return_true_when_are_not_equals_receives_two_values_are_not_equal_regardless_of_the_type(input, expected):
+    contract =  CommonsValidationContract()
+    contract.are_not_equals(input, expected, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid
+
+def test_should_return_true_when_is_none_receives_value_none():
+    contract =  CommonsValidationContract()
+    contract.is_none(None, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid
+
+def test_should_return_false_when_is_none_receives_value_is_not_none():
+    contract =  CommonsValidationContract()
+    contract.is_none(fake.text(max_nb_chars=10), fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid is False
+
+def test_should_return_true_when_is_not_none_receives_value_is_not_none():
+    contract =  CommonsValidationContract()
+    contract.is_not_none(fake.text(max_nb_chars=10), fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid
+
+def test_should_return_false_when_is_not_none_receives_value_is_none():
+    contract =  CommonsValidationContract()
+    contract.is_not_none(None, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+
+    assert contract.is_valid is False
diff --git a/tests/validations/test_contract.py b/tests/validations/test_contract.py
new file mode 100644
index 0000000..04c32de
--- /dev/null
+++ b/tests/validations/test_contract.py
@@ -0,0 +1,46 @@
+import pytest
+from uuid import uuid4
+from faker import Faker
+
+from flunt.validations.contract import Contract
+
+
+fake = Faker()
+
+@pytest.mark.parametrize(
+	'input',
+	[
+		fake.uuid4(),
+		fake.date_object(),
+        fake.json(),
+        fake.pydecimal(),
+        fake.pydict(),
+        fake.pyfloat(),
+        fake.pyint(),
+        fake.pyiterable(),
+        fake.pylist(),
+        fake.pyset(),
+        fake.pystr(),
+        fake.pystr_format(),
+        fake.pystruct(),
+        fake.pytuple(),
+        True,
+        False,
+        uuid4()
+	],
+)
+def test_should_be_valid_and_not_return_notification_when_required_is_not_none(input):
+    contract = Contract().requires(input, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+    assert contract.is_valid
+
+
+@pytest.mark.parametrize(
+	'input',
+	[
+		None,
+        '',
+	],
+)
+def test_should_be_invalid_and_return_once_notification_when_required_is_none_or_empty(input):
+    contract = Contract().requires(input, fake.text(max_nb_chars=10), fake.text(max_nb_chars=20))
+    assert contract.is_valid is False
diff --git a/tests/validations/test_credit_card_validation_contract.py b/tests/validations/test_credit_card_validation_contract.py
index 742bcd4..7327bda 100644
--- a/tests/validations/test_credit_card_validation_contract.py
+++ b/tests/validations/test_credit_card_validation_contract.py
@@ -1,11 +1,10 @@
 import pytest
 from faker import Faker
 
-from flunt.validations.contract import Contract
+from flunt.validations.credit_card_validation_contract import CreditCardValidationContract
 
 fake = Faker()
 
-
 @pytest.mark.parametrize(
 	'input,expect',
 	[
@@ -17,7 +16,7 @@
 def test_should_not_receive_a_notification_when_the_credit_card_number_is_valid(
 	input, expect
 ):
-	contract = Contract().is_credit_card(
+	contract = CreditCardValidationContract().is_credit_card(
 		input, 'CreditCard', 'Value should return a valid Credit Card Number'
 	)
 	assert len(contract.get_notifications()) == expect
@@ -35,7 +34,7 @@ def test_should_not_receive_a_notification_when_the_credit_card_number_is_valid(
 def test_should_receive_a_notification_when_the_credit_card_number_is_invalid(
 	input, expect
 ):
-	contract = Contract().is_credit_card(
+	contract = CreditCardValidationContract().is_credit_card(
 		input, 'CreditCard', 'Value should return a valid Credit Card Number'
 	)
 	assert len(contract.get_notifications()) == expect
diff --git a/tests/validations/test_email_validation_contract.py b/tests/validations/test_email_validation_contract.py
index 031de16..caef3bf 100644
--- a/tests/validations/test_email_validation_contract.py
+++ b/tests/validations/test_email_validation_contract.py
@@ -1,22 +1,22 @@
-from flunt.validations.contract import Contract
+import pytest
+from flunt.validations.email_validation_contract import EmailValidationContract
 
-message = 'Custom message here'
+@pytest.fixture()
+def message():
+    yield 'Custom message here'
 
-
-def test_should_be_valid_when_correct_email(entity_mock):
+def test_should_be_valid_when_correct_email(entity_mock, message):
 	contract = (
-		Contract()
-		.requires(entity_mock.email_valid, 'Email', message)
+		EmailValidationContract()
 		.is_email(entity_mock.email_valid, 'email', message)
 	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
-def test_should_return_a_once_notification_when_email_is_invalid(entity_mock):
+def test_should_return_a_once_notification_when_email_is_invalid(entity_mock, message):
 	contract = (
-		Contract()
-		.requires(entity_mock.email_invalid, 'Email', message)
+		EmailValidationContract()
 		.is_email(entity_mock.email_invalid, 'email', message)
 	)
 	assert len(contract.get_notifications()) == 1
diff --git a/tests/validations/test_string_validation_contract.py b/tests/validations/test_string_validation_contract.py
index eac23ef..ee961ac 100644
--- a/tests/validations/test_string_validation_contract.py
+++ b/tests/validations/test_string_validation_contract.py
@@ -1,131 +1,145 @@
-from flunt.validations.contract import Contract
+import pytest
 from tests.mocks.entity.sample_entity import SampleEntity
+from flunt.validations.strings_validation_contract import StringValidationContract
 
 
-def test_should_be_valid_when_is_lower_than_20(entity_mock: SampleEntity):
-	contract = Contract().is_lower_than(
-		entity_mock.first_name, 20, 'first_name', 'any message'
+@pytest.fixture()
+def key():
+    yield 'first_name'
+
+
+@pytest.fixture()
+def message():
+	yield 'any message'
+
+
+def test_should_be_valid_when_is_lower_than_20(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().is_lower_than(
+		entity_mock.first_name, 20, key, message
 	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
 def test_should_be_invalid_and_return_once_notification_when_not_is_lower_than_5(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_lower_than(
-		entity_mock.first_name, 5, 'first_name', 'any message'
+	contract = StringValidationContract().is_lower_than(
+		entity_mock.first_name, 5, key, message
 	)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
 def test_should_be_valid_when_is_lower_or_equals_than_20(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_lower_or_equals_than(
-		entity_mock.first_name, 20, 'first_name', 'any message'
+	contract = StringValidationContract().is_lower_or_equals_than(
+		entity_mock.first_name, 20, key, message
 	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
 def test_should_be_invalid_and_return_once_notification_when_not_is_lower_or_equals_than_14(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_lower_or_equals_than(
-		entity_mock.first_name, 14, 'first_name', 'any message'
+	contract = StringValidationContract().is_lower_or_equals_than(
+		entity_mock.first_name, 14, key, message
 	)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
-def test_should_be_valid_when_is_greater_than_10(entity_mock: SampleEntity):
-	contract = Contract().is_greater_than(
-		entity_mock.first_name, 10, 'first_name', 'any message'
+def test_should_be_valid_when_is_greater_than_10(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().is_greater_than(
+		entity_mock.first_name, 10, key, message
 	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
 def test_should_be_invalid_and_return_once_notification_when_not_is_greater_than_15(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_greater_than(
-		entity_mock.first_name, 15, 'first_name', 'any message'
+	contract = StringValidationContract().is_greater_than(
+		entity_mock.first_name, 15, key, message
 	)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
 def test_should_be_valid_when_is_greater_or_equals_than_13(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_greater_or_equals_than(
-		entity_mock.first_name, 13, 'first_name', 'any message'
+	contract = StringValidationContract().is_greater_or_equals_than(
+		entity_mock.first_name, 13, key, message
 	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
 def test_should_be_invalid_and_return_once_notification_when_not_is_greater_or_equals_than_14(
-	entity_mock: SampleEntity,
+	entity_mock: SampleEntity, key, message
 ):
-	contract = Contract().is_greater_or_equals_than(
-		entity_mock.first_name, 14, 'first_name', 'any message'
+	contract = StringValidationContract().is_greater_or_equals_than(
+		entity_mock.first_name, 14, key, message
 	)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
-def test_should_be_valid_when_is_none():
-	contract = Contract().is_none(None, 'first_name', 'any message')  # type: ignore
+def test_should_be_valid_when_is_not_none_or_white_space(
+	entity_mock: SampleEntity, key, message
+):
+	contract = StringValidationContract().is_not_none_or_white_space(
+		entity_mock.first_name, key, message
+	)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
-def test_should_be_invalid_and_return_once_notification_when_not_is_none(
-	entity_mock: SampleEntity,
-):
-	contract = Contract().is_none(entity_mock.first_name, 'first_name', 'any message')
+def test_should_be_invalid_and_return_once_notification_when_is_none(key, message):
+	contract = StringValidationContract().is_not_none_or_white_space(None, key, message)  # type: ignore
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
-def test_should_be_valid_when_is_not_none(entity_mock: SampleEntity):
-	contract = Contract().is_not_none(
-		entity_mock.first_name, 'first_name', 'any message'
-	)
+def test_should_be_invalid_and_return_once_notification_when_white_space(key, message):
+	contract = StringValidationContract().is_not_none_or_white_space(' ', key, message)
+	assert contract.is_valid is False
+	assert len(contract.get_notifications()) == 1
+
+
+def test_should_be_valid_and_not_return_notification_when_contains_a_search_string(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().contains(entity_mock.first_name, "any", key, message)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
-def test_should_be_invalid_and_return_once_notification_when_not_none():
-	contract = Contract().is_not_none(None, 'first_name', 'any message')  # type: ignore
+def test_should_be_invalid_and_return_once_notification_when_not_contains_a_search_string(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().contains(entity_mock.first_name, "bla", key, message)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
-def test_should_be_valid_when_is_not_none_or_white_space(
-	entity_mock: SampleEntity,
-):
-	contract = Contract().is_not_none_or_white_space(
-		entity_mock.first_name, 'first_name', 'any message'
-	)
+def test_should_be_valid_and_not_return_notification_when_not_contains_a_search_string(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().not_contains(entity_mock.first_name, "bla", key, message)
 	assert contract.is_valid
 	assert len(contract.get_notifications()) == 0
 
 
-def test_should_be_invalid_and_return_once_notification_when_is_none():
-	contract = Contract().is_not_none_or_white_space(None, 'first_name', 'any message')  # type: ignore
+def test_should_be_invalid_and_return_once_notification_when_contains_a_search_string(entity_mock: SampleEntity, key, message):
+	contract = StringValidationContract().not_contains(entity_mock.first_name, "any", key, message)
 	assert contract.is_valid is False
 	assert len(contract.get_notifications()) == 1
 
 
-def test_should_be_invalid_and_return_once_notification_when_white_space():
-	contract = Contract().is_not_none_or_white_space(' ', 'first_name', 'any message')
-	assert contract.is_valid is False
-	assert len(contract.get_notifications()) == 1
+def test_should_be_valid_and_not_return_notification_when_value_is_between():
+	contract = StringValidationContract().is_between("any_text", 1, 10, "test", "any_message")
+	assert contract.is_valid
 
 
-# TODO: Create tests Equal, Not Equal, Contain, Not Contain and Between
+def test_should_be_invalid_and_return_once_notification_when_value_is_not_between():
+	contract = StringValidationContract().is_between("any_text", 10, 20, "test", "any_message")
+	assert contract.is_valid is False