From 47c9f019fb2874501b2535464ba40b66a294490a Mon Sep 17 00:00:00 2001 From: Davlat Davydov Date: Wed, 15 Jul 2026 21:15:26 +0300 Subject: [PATCH] feat(all): remove old A/A flow feat(all): remove old A/A flow chore(infra): moved migrations feat(infra): keycloak feat(infra): OAuth-Proxy feat(infra): set authorization token chore(all): rename project from prodik to conode feat(infra): application migration test: mock authorization flow ci: some changes some changes some changes some changes some changes some changes --- .github/workflows/workflow.yml | 38 +- Dockerfile | 2 +- README.md | 2 +- deploy/init.sql | 27 + deploy/keycloak.env | 8 + deploy/keycloak.json | 3099 +++++++++++++++++ deploy/nginx.conf | 15 - deploy/oauth_proxy.toml | 25 + deploy/postgres.env | 8 +- docker-compose.base.yaml | 125 +- docker-compose.dev.yaml | 22 +- docker-compose.prod.yaml | 9 +- example.config.toml | 10 +- pyproject.toml | 20 +- .../application/__init__.py | 0 .../attach_node_to_group/__init__.py | 0 .../attach_node_to_group/attach_node.py | 12 +- .../detach_node_from_group/__init__.py | 0 .../detach_node_from_group/detach_node.py | 10 +- src/{prodik => conode}/application/errors.py | 0 .../application/interfaces/__init__.py | 0 .../interfaces/identity_provider.py | 2 +- .../application/interfaces/password_hasher.py | 0 .../interfaces/repositories/__init__.py | 3 - .../interfaces/repositories/company.py | 4 +- .../interfaces/repositories/context.py | 4 +- .../interfaces/repositories/contract.py | 2 +- .../interfaces/repositories/edge.py | 6 +- .../interfaces/repositories/grant/__init__.py | 0 .../interfaces/repositories/grant/company.py | 0 .../interfaces/repositories/grant/user.py | 6 +- .../interfaces/repositories/group.py | 4 +- .../interfaces/repositories/node.py | 6 +- .../interfaces/repositories/offer.py | 2 +- .../interfaces/repositories/permissions.py | 2 +- .../interfaces/repositories/role.py | 4 +- .../interfaces/repositories/user.py | 2 +- .../interfaces/token_manager/__init__.py | 11 + .../interfaces/token_manager/token_manager.py | 21 + .../interfaces/transaction_manager.py | 0 .../application/manage_context/__init__.py | 0 .../manage_context/create_context.py | 10 +- .../manage_context/delete_context.py | 8 +- .../application/manage_edge/__init__.py | 0 .../application/manage_edge/create_edge.py | 14 +- .../application/manage_edge/delete_edge.py | 8 +- .../application/manage_group/__init__.py | 0 .../application/manage_group/create_group.py | 10 +- .../application/manage_group/delete_group.py | 8 +- .../application/manage_node/__init__.py | 0 .../application/manage_node/create_node.py | 10 +- .../application/manage_node/delete_node.py | 8 +- .../application/manage_profile/__init__.py | 0 .../update_current_user_profile.py | 17 +- .../application/manage_role/__init__.py | 0 .../application/manage_role/create_role.py | 12 +- .../application/manage_role/delete_role.py | 8 +- .../application/manage_role/update_role.py | 8 +- .../manage_user_rights/__init__.py | 0 .../manage_user_rights/give_role_to_user.py | 14 +- .../revoke_role_from_user.py | 12 +- .../receive_context_info/__init__.py | 0 .../receive_context_info/get_context_by_id.py | 6 +- .../receive_graph_statistics/__init__.py | 0 .../receive_graph_statistics/neighbours.py | 12 +- .../receive_graph_statistics/shortest_path.py | 10 +- .../receive_group_info/__init__.py | 0 .../receive_group_info/get_group_by_id.py | 6 +- .../application/receive_node_info/__init__.py | 0 .../receive_node_info/get_nodes_by_group.py | 8 +- .../application/receive_user_info/__init__.py | 0 .../receive_user_info/get_current_user.py | 4 +- .../receive_user_info/get_user_by_username.py | 8 +- .../application/register_company/__init__.py | 0 .../register_company/register_company.py | 14 +- .../application/services/__init__.py | 0 .../application/services/access_control.py | 40 +- .../application/services/offer_acceptance.py | 8 +- .../application/services/offer_sending.py | 10 +- .../application/services/role_managment.py | 4 +- .../application/share_graph/__init__.py | 0 .../application/share_graph/accept_offer.py | 16 +- .../application/share_graph/decline_offer.py | 10 +- .../share_graph/send_offer_to_company.py | 18 +- .../update_edge_weight/__init__.py | 0 .../decrement_edge_weight.py | 8 +- .../increment_edge_weight.py | 8 +- .../update_edge_weight/update_edge_weight.py | 8 +- .../application/update_node/__init__.py | 0 .../application/update_node/update_node.py | 8 +- .../application/verify_company/__init__.py | 0 .../verify_company/verify_company.py | 10 +- src/{prodik => conode}/bootstrap/__init__.py | 0 .../bootstrap/api/__init__.py | 0 src/{prodik => conode}/bootstrap/api/run.py | 8 +- src/{prodik => conode}/bootstrap/cli.py | 11 +- .../bootstrap/di/__init__.py | 0 .../bootstrap/di/container.py | 10 +- .../bootstrap/di/providers/__init__.py | 4 +- .../bootstrap/di/providers/application.py | 50 +- .../bootstrap/di/providers/connection.py | 18 +- .../bootstrap/di/providers/infrastructure.py | 16 +- src/conode/bootstrap/di/providers/secrets.py | 10 + src/{prodik => conode}/bootstrap/logs.py | 0 src/{prodik => conode}/domain/__init__.py | 0 .../domain/company/__init__.py | 0 .../domain/company/errors.py | 2 +- .../domain/company/model.py | 6 +- .../domain/context/__init__.py | 0 .../domain/context/errors.py | 2 +- .../domain/context/model.py | 6 +- .../domain/contract/__init__.py | 0 .../domain/contract/errors.py | 2 +- .../domain/contract/model.py | 10 +- .../domain/edge/__init__.py | 0 src/{prodik => conode}/domain/edge/errors.py | 2 +- src/{prodik => conode}/domain/edge/model.py | 10 +- .../domain/grant/__init__.py | 0 .../domain/grant/company.py | 6 +- src/{prodik => conode}/domain/grant/user.py | 6 +- .../domain/group/__init__.py | 0 src/{prodik => conode}/domain/group/errors.py | 2 +- src/{prodik => conode}/domain/group/model.py | 6 +- .../domain/node/__init__.py | 0 src/{prodik => conode}/domain/node/errors.py | 2 +- src/{prodik => conode}/domain/node/model.py | 8 +- .../domain/offer/__init__.py | 0 src/{prodik => conode}/domain/offer/errors.py | 2 +- src/{prodik => conode}/domain/offer/model.py | 12 +- .../domain/role/__init__.py | 0 src/{prodik => conode}/domain/role/errors.py | 2 +- src/{prodik => conode}/domain/role/model.py | 10 +- .../domain/shared/__init__.py | 0 .../domain/shared/entity.py | 0 .../domain/shared/value_object.py | 0 .../domain/user/__init__.py | 0 src/{prodik => conode}/domain/user/errors.py | 2 +- src/{prodik => conode}/domain/user/model.py | 11 +- .../infrastructure/__init__.py | 0 .../infrastructure/config.py | 19 +- .../infrastructure/identity_provider.py | 38 + .../infrastructure/password_hasher.py | 2 +- .../infrastructure/persistence/__init__.py | 0 .../infrastructure/persistence/alembic.ini | 0 .../persistence/migrations/README | 0 .../persistence/migrations/__init__.py | 0 .../persistence/migrations/env.py | 4 +- .../persistence/migrations/script.py.mako | 0 .../versions/0333150881b1_parent_group_id.py | 0 .../versions/080a7bac6dfb_custom_type.py | 0 .../1786a4b3cea6_remove_company_id.py | 0 .../2e3b8d977df1_offers_and_contracts.py | 0 .../migrations/versions/3057b93fb0cb_bio.py | 0 .../versions/33f30ca8ca69_company.py | 0 .../versions/4a52d766f55e_nullable_from.py | 0 .../versions/4a8ee55b96fa_unique_constrait.py | 0 .../504f086cc1a2_permission_type_offer.py | 0 .../5192c880eb4e_edge_unique_constrait.py | 0 .../versions/6240bee5cfae_remove_owner_id.py | 0 .../versions/658eaeeb4857_edge_company_id.py | 0 ...046693f2e1_renamed_hashed_password_name.py | 0 .../versions/6c7e8a84a7c8_grants.py | 0 .../74a91a825965_remove_old_authorization.py | 53 + .../78521bfa1ec2_contexts_and_edges.py | 0 .../versions/88413fda6e9e_initial.py | 0 .../versions/b5ff44a6d487_nodes_and_groups.py | 0 .../versions/d221025bd616_role_name.py | 0 .../e60ec6d4a0a5_roles_and_permissions.py | 0 .../ee772395dc0d_association_cascade.py | 0 .../versions/ee89ddc73033_contexts.py | 0 .../infrastructure/persistence/registry.py | 52 +- .../infrastructure/persistence/types.py | 16 +- .../infrastructure/repositories/__init__.py | 3 - .../infrastructure/repositories/company.py | 8 +- .../infrastructure/repositories/context.py | 8 +- .../infrastructure/repositories/contract.py | 4 +- .../infrastructure/repositories/edge.py | 10 +- .../repositories/grant/__init__.py | 0 .../infrastructure/repositories/grant/user.py | 8 +- .../infrastructure/repositories/group.py | 8 +- .../infrastructure/repositories/node.py | 10 +- .../infrastructure/repositories/offer.py | 6 +- .../infrastructure/repositories/permission.py | 6 +- .../infrastructure/repositories/role.py | 10 +- .../infrastructure/repositories/user.py | 9 +- src/conode/infrastructure/token_manager.py | 39 + .../infrastructure/transaction_manager.py | 2 +- .../presentation/__init__.py | 0 src/{prodik => conode}/presentation/common.py | 10 +- .../presentation/exceptions/__init__.py | 0 .../presentation/exceptions/handlers.py | 16 +- .../presentation/middlewares/__init__.py | 0 .../presentation/middlewares/log.py | 0 .../presentation/schemas/__init__.py | 0 .../presentation/schemas/company.py | 6 +- .../presentation/schemas/context.py | 6 +- .../presentation/schemas/edge.py | 8 +- .../presentation/schemas/group.py | 6 +- .../presentation/schemas/node.py | 8 +- .../presentation/schemas/offer.py | 10 +- .../presentation/schemas/role.py | 6 +- .../presentation/schemas/user.py | 12 +- .../presentation/views/__init__.py | 2 - .../presentation/views/company.py | 8 +- .../presentation/views/context.py | 6 +- .../presentation/views/edge.py | 14 +- .../presentation/views/group.py | 6 +- .../presentation/views/node.py | 22 +- .../presentation/views/offer.py | 6 +- .../presentation/views/role.py | 6 +- .../presentation/views/root.py | 0 .../presentation/views/user.py | 37 +- src/{prodik => conode}/py.typed | 0 .../application/authorization/__init__.py | 11 - src/prodik/application/authorization/login.py | 102 - .../application/authorization/refresh.py | 47 - .../application/authorization/register.py | 105 - .../repositories/authorization/__init__.py | 7 - .../authorization/local_authorization.py | 11 - .../repositories/authorization/session.py | 13 - .../interfaces/token_managers/__init__.py | 13 - .../token_managers/access_token_manager.py | 19 - .../token_managers/refresh_token_manager.py | 5 - .../manage_credentials/__init__.py | 6 - .../update_current_user_password.py | 103 - src/prodik/bootstrap/di/providers/cache.py | 20 - src/prodik/domain/authorization/__init__.py | 12 - .../domain/authorization/local/__init__.py | 6 - .../domain/authorization/local/model.py | 34 - .../domain/authorization/oauth/__init__.py | 6 - .../domain/authorization/oauth/model.py | 24 - .../domain/authorization/session/__init__.py | 6 - .../domain/authorization/session/model.py | 32 - .../infrastructure/identity_provider.py | 60 - .../repositories/authorization/__init__.py | 7 - .../authorization/local_authorization.py | 76 - .../repositories/authorization/session.py | 118 - .../infrastructure/token_managers/__init__.py | 7 - .../token_managers/access_token_manager.py | 43 - .../token_managers/refresh_token_manager.py | 10 - src/prodik/presentation/schemas/auth.py | 53 - src/prodik/presentation/views/auth.py | 75 - tests/conftest.py | 108 +- tests/e2e/authentication/__init__.py | 0 tests/e2e/authentication/test_login.py | 70 - .../e2e/authentication/test_refresh_token.py | 68 - tests/e2e/authentication/test_register.py | 71 - tests/e2e/company/test_register_company.py | 2 +- tests/e2e/company/test_verify_company.py | 2 +- tests/e2e/context/test_create_context.py | 4 +- tests/e2e/context/test_delete_context.py | 2 +- tests/e2e/edge/test_create_edge.py | 2 +- tests/e2e/edge/test_delete_edge.py | 2 +- tests/e2e/edge/test_manipulate_edge_weight.py | 2 +- tests/e2e/group/test_create_group.py | 2 +- tests/e2e/group/test_delete_group.py | 2 +- tests/e2e/helpers/asserts.py | 6 +- tests/e2e/node/test_attach_node.py | 2 +- tests/e2e/node/test_create_node.py | 4 +- tests/e2e/node/test_delete_node.py | 2 +- tests/e2e/node/test_detach_node.py | 2 +- tests/e2e/node/test_update_node.py | 4 +- tests/e2e/user/test_update_current_profile.py | 5 +- tests/factories/common.py | 2 +- tests/factories/models/company.py | 14 +- tests/factories/models/context.py | 8 +- tests/factories/models/edge.py | 12 +- tests/factories/models/group.py | 8 +- tests/factories/models/node.py | 10 +- tests/factories/models/role.py | 14 +- tests/factories/models/user.py | 54 +- tests/factories/schemas/__init__.py | 10 - tests/factories/schemas/authentication.py | 17 - tests/factories/schemas/company.py | 2 +- tests/factories/schemas/context.py | 2 +- tests/factories/schemas/edge.py | 2 +- tests/factories/schemas/group.py | 2 +- tests/factories/schemas/node.py | 2 +- tests/factories/schemas/user.py | 8 +- uv.lock | 1641 ++++++++- 280 files changed, 5528 insertions(+), 2293 deletions(-) create mode 100644 deploy/init.sql create mode 100644 deploy/keycloak.env create mode 100644 deploy/keycloak.json delete mode 100644 deploy/nginx.conf create mode 100644 deploy/oauth_proxy.toml rename src/{prodik => conode}/application/__init__.py (100%) rename src/{prodik => conode}/application/attach_node_to_group/__init__.py (100%) rename src/{prodik => conode}/application/attach_node_to_group/attach_node.py (85%) rename src/{prodik => conode}/application/detach_node_from_group/__init__.py (100%) rename src/{prodik => conode}/application/detach_node_from_group/detach_node.py (82%) rename src/{prodik => conode}/application/errors.py (100%) rename src/{prodik => conode}/application/interfaces/__init__.py (100%) rename src/{prodik => conode}/application/interfaces/identity_provider.py (70%) rename src/{prodik => conode}/application/interfaces/password_hasher.py (100%) rename src/{prodik => conode}/application/interfaces/repositories/__init__.py (87%) rename src/{prodik => conode}/application/interfaces/repositories/company.py (82%) rename src/{prodik => conode}/application/interfaces/repositories/context.py (81%) rename src/{prodik => conode}/application/interfaces/repositories/contract.py (74%) rename src/{prodik => conode}/application/interfaces/repositories/edge.py (82%) rename src/{prodik => conode}/application/interfaces/repositories/grant/__init__.py (100%) rename src/{prodik => conode}/application/interfaces/repositories/grant/company.py (100%) rename src/{prodik => conode}/application/interfaces/repositories/grant/user.py (77%) rename src/{prodik => conode}/application/interfaces/repositories/group.py (81%) rename src/{prodik => conode}/application/interfaces/repositories/node.py (88%) rename src/{prodik => conode}/application/interfaces/repositories/offer.py (93%) rename src/{prodik => conode}/application/interfaces/repositories/permissions.py (89%) rename src/{prodik => conode}/application/interfaces/repositories/role.py (82%) rename src/{prodik => conode}/application/interfaces/repositories/user.py (89%) create mode 100644 src/conode/application/interfaces/token_manager/__init__.py create mode 100644 src/conode/application/interfaces/token_manager/token_manager.py rename src/{prodik => conode}/application/interfaces/transaction_manager.py (100%) rename src/{prodik => conode}/application/manage_context/__init__.py (100%) rename src/{prodik => conode}/application/manage_context/create_context.py (80%) rename src/{prodik => conode}/application/manage_context/delete_context.py (77%) rename src/{prodik => conode}/application/manage_edge/__init__.py (100%) rename src/{prodik => conode}/application/manage_edge/create_edge.py (85%) rename src/{prodik => conode}/application/manage_edge/delete_edge.py (80%) rename src/{prodik => conode}/application/manage_group/__init__.py (100%) rename src/{prodik => conode}/application/manage_group/create_group.py (84%) rename src/{prodik => conode}/application/manage_group/delete_group.py (77%) rename src/{prodik => conode}/application/manage_node/__init__.py (100%) rename src/{prodik => conode}/application/manage_node/create_node.py (85%) rename src/{prodik => conode}/application/manage_node/delete_node.py (84%) rename src/{prodik => conode}/application/manage_profile/__init__.py (100%) rename src/{prodik => conode}/application/manage_profile/update_current_user_profile.py (53%) rename src/{prodik => conode}/application/manage_role/__init__.py (100%) rename src/{prodik => conode}/application/manage_role/create_role.py (88%) rename src/{prodik => conode}/application/manage_role/delete_role.py (74%) rename src/{prodik => conode}/application/manage_role/update_role.py (91%) rename src/{prodik => conode}/application/manage_user_rights/__init__.py (100%) rename src/{prodik => conode}/application/manage_user_rights/give_role_to_user.py (81%) rename src/{prodik => conode}/application/manage_user_rights/revoke_role_from_user.py (83%) rename src/{prodik => conode}/application/receive_context_info/__init__.py (100%) rename src/{prodik => conode}/application/receive_context_info/get_context_by_id.py (74%) rename src/{prodik => conode}/application/receive_graph_statistics/__init__.py (100%) rename src/{prodik => conode}/application/receive_graph_statistics/neighbours.py (86%) rename src/{prodik => conode}/application/receive_graph_statistics/shortest_path.py (91%) rename src/{prodik => conode}/application/receive_group_info/__init__.py (100%) rename src/{prodik => conode}/application/receive_group_info/get_group_by_id.py (74%) rename src/{prodik => conode}/application/receive_node_info/__init__.py (100%) rename src/{prodik => conode}/application/receive_node_info/get_nodes_by_group.py (82%) rename src/{prodik => conode}/application/receive_user_info/__init__.py (100%) rename src/{prodik => conode}/application/receive_user_info/get_current_user.py (71%) rename src/{prodik => conode}/application/receive_user_info/get_user_by_username.py (71%) rename src/{prodik => conode}/application/register_company/__init__.py (100%) rename src/{prodik => conode}/application/register_company/register_company.py (87%) rename src/{prodik => conode}/application/services/__init__.py (100%) rename src/{prodik => conode}/application/services/access_control.py (88%) rename src/{prodik => conode}/application/services/offer_acceptance.py (89%) rename src/{prodik => conode}/application/services/offer_sending.py (91%) rename src/{prodik => conode}/application/services/role_managment.py (94%) rename src/{prodik => conode}/application/share_graph/__init__.py (100%) rename src/{prodik => conode}/application/share_graph/accept_offer.py (94%) rename src/{prodik => conode}/application/share_graph/decline_offer.py (87%) rename src/{prodik => conode}/application/share_graph/send_offer_to_company.py (90%) rename src/{prodik => conode}/application/update_edge_weight/__init__.py (100%) rename src/{prodik => conode}/application/update_edge_weight/decrement_edge_weight.py (80%) rename src/{prodik => conode}/application/update_edge_weight/increment_edge_weight.py (80%) rename src/{prodik => conode}/application/update_edge_weight/update_edge_weight.py (81%) rename src/{prodik => conode}/application/update_node/__init__.py (100%) rename src/{prodik => conode}/application/update_node/update_node.py (86%) rename src/{prodik => conode}/application/verify_company/__init__.py (100%) rename src/{prodik => conode}/application/verify_company/verify_company.py (79%) rename src/{prodik => conode}/bootstrap/__init__.py (100%) rename src/{prodik => conode}/bootstrap/api/__init__.py (100%) rename src/{prodik => conode}/bootstrap/api/run.py (87%) rename src/{prodik => conode}/bootstrap/cli.py (88%) rename src/{prodik => conode}/bootstrap/di/__init__.py (100%) rename src/{prodik => conode}/bootstrap/di/container.py (75%) rename src/{prodik => conode}/bootstrap/di/providers/__init__.py (76%) rename src/{prodik => conode}/bootstrap/di/providers/application.py (60%) rename src/{prodik => conode}/bootstrap/di/providers/connection.py (73%) rename src/{prodik => conode}/bootstrap/di/providers/infrastructure.py (71%) create mode 100644 src/conode/bootstrap/di/providers/secrets.py rename src/{prodik => conode}/bootstrap/logs.py (100%) rename src/{prodik => conode}/domain/__init__.py (100%) rename src/{prodik => conode}/domain/company/__init__.py (100%) rename src/{prodik => conode}/domain/company/errors.py (79%) rename src/{prodik => conode}/domain/company/model.py (94%) rename src/{prodik => conode}/domain/context/__init__.py (100%) rename src/{prodik => conode}/domain/context/errors.py (79%) rename src/{prodik => conode}/domain/context/model.py (92%) rename src/{prodik => conode}/domain/contract/__init__.py (100%) rename src/{prodik => conode}/domain/contract/errors.py (71%) rename src/{prodik => conode}/domain/contract/model.py (89%) rename src/{prodik => conode}/domain/edge/__init__.py (100%) rename src/{prodik => conode}/domain/edge/errors.py (78%) rename src/{prodik => conode}/domain/edge/model.py (89%) rename src/{prodik => conode}/domain/grant/__init__.py (100%) rename src/{prodik => conode}/domain/grant/company.py (82%) rename src/{prodik => conode}/domain/grant/user.py (81%) rename src/{prodik => conode}/domain/group/__init__.py (100%) rename src/{prodik => conode}/domain/group/errors.py (83%) rename src/{prodik => conode}/domain/group/model.py (94%) rename src/{prodik => conode}/domain/node/__init__.py (100%) rename src/{prodik => conode}/domain/node/errors.py (78%) rename src/{prodik => conode}/domain/node/model.py (93%) rename src/{prodik => conode}/domain/offer/__init__.py (100%) rename src/{prodik => conode}/domain/offer/errors.py (89%) rename src/{prodik => conode}/domain/offer/model.py (95%) rename src/{prodik => conode}/domain/role/__init__.py (100%) rename src/{prodik => conode}/domain/role/errors.py (69%) rename src/{prodik => conode}/domain/role/model.py (91%) rename src/{prodik => conode}/domain/shared/__init__.py (100%) rename src/{prodik => conode}/domain/shared/entity.py (100%) rename src/{prodik => conode}/domain/shared/value_object.py (100%) rename src/{prodik => conode}/domain/user/__init__.py (100%) rename src/{prodik => conode}/domain/user/errors.py (86%) rename src/{prodik => conode}/domain/user/model.py (93%) rename src/{prodik => conode}/infrastructure/__init__.py (100%) rename src/{prodik => conode}/infrastructure/config.py (82%) create mode 100644 src/conode/infrastructure/identity_provider.py rename src/{prodik => conode}/infrastructure/password_hasher.py (89%) rename src/{prodik => conode}/infrastructure/persistence/__init__.py (100%) rename src/{prodik => conode}/infrastructure/persistence/alembic.ini (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/README (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/__init__.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/env.py (96%) rename src/{prodik => conode}/infrastructure/persistence/migrations/script.py.mako (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/0333150881b1_parent_group_id.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/080a7bac6dfb_custom_type.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/1786a4b3cea6_remove_company_id.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/2e3b8d977df1_offers_and_contracts.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/3057b93fb0cb_bio.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/33f30ca8ca69_company.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/4a52d766f55e_nullable_from.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/4a8ee55b96fa_unique_constrait.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/504f086cc1a2_permission_type_offer.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/5192c880eb4e_edge_unique_constrait.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/6240bee5cfae_remove_owner_id.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/658eaeeb4857_edge_company_id.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/6c046693f2e1_renamed_hashed_password_name.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/6c7e8a84a7c8_grants.py (100%) create mode 100644 src/conode/infrastructure/persistence/migrations/versions/74a91a825965_remove_old_authorization.py rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/78521bfa1ec2_contexts_and_edges.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/88413fda6e9e_initial.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/b5ff44a6d487_nodes_and_groups.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/d221025bd616_role_name.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/e60ec6d4a0a5_roles_and_permissions.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/ee772395dc0d_association_cascade.py (100%) rename src/{prodik => conode}/infrastructure/persistence/migrations/versions/ee89ddc73033_contexts.py (100%) rename src/{prodik => conode}/infrastructure/persistence/registry.py (87%) rename src/{prodik => conode}/infrastructure/persistence/types.py (84%) rename src/{prodik => conode}/infrastructure/repositories/__init__.py (87%) rename src/{prodik => conode}/infrastructure/repositories/company.py (94%) rename src/{prodik => conode}/infrastructure/repositories/context.py (92%) rename src/{prodik => conode}/infrastructure/repositories/contract.py (90%) rename src/{prodik => conode}/infrastructure/repositories/edge.py (94%) rename src/{prodik => conode}/infrastructure/repositories/grant/__init__.py (100%) rename src/{prodik => conode}/infrastructure/repositories/grant/user.py (91%) rename src/{prodik => conode}/infrastructure/repositories/group.py (92%) rename src/{prodik => conode}/infrastructure/repositories/node.py (97%) rename src/{prodik => conode}/infrastructure/repositories/offer.py (97%) rename src/{prodik => conode}/infrastructure/repositories/permission.py (95%) rename src/{prodik => conode}/infrastructure/repositories/role.py (92%) rename src/{prodik => conode}/infrastructure/repositories/user.py (91%) create mode 100644 src/conode/infrastructure/token_manager.py rename src/{prodik => conode}/infrastructure/transaction_manager.py (94%) rename src/{prodik => conode}/presentation/__init__.py (100%) rename src/{prodik => conode}/presentation/common.py (78%) rename src/{prodik => conode}/presentation/exceptions/__init__.py (100%) rename src/{prodik => conode}/presentation/exceptions/handlers.py (88%) rename src/{prodik => conode}/presentation/middlewares/__init__.py (100%) rename src/{prodik => conode}/presentation/middlewares/log.py (100%) rename src/{prodik => conode}/presentation/schemas/__init__.py (100%) rename src/{prodik => conode}/presentation/schemas/company.py (86%) rename src/{prodik => conode}/presentation/schemas/context.py (83%) rename src/{prodik => conode}/presentation/schemas/edge.py (67%) rename src/{prodik => conode}/presentation/schemas/group.py (85%) rename src/{prodik => conode}/presentation/schemas/node.py (86%) rename src/{prodik => conode}/presentation/schemas/offer.py (73%) rename src/{prodik => conode}/presentation/schemas/role.py (91%) rename src/{prodik => conode}/presentation/schemas/user.py (80%) rename src/{prodik => conode}/presentation/views/__init__.py (90%) rename src/{prodik => conode}/presentation/views/company.py (83%) rename src/{prodik => conode}/presentation/views/context.py (87%) rename src/{prodik => conode}/presentation/views/edge.py (89%) rename src/{prodik => conode}/presentation/views/group.py (88%) rename src/{prodik => conode}/presentation/views/node.py (87%) rename src/{prodik => conode}/presentation/views/offer.py (92%) rename src/{prodik => conode}/presentation/views/role.py (95%) rename src/{prodik => conode}/presentation/views/root.py (100%) rename src/{prodik => conode}/presentation/views/user.py (67%) rename src/{prodik => conode}/py.typed (100%) delete mode 100644 src/prodik/application/authorization/__init__.py delete mode 100644 src/prodik/application/authorization/login.py delete mode 100644 src/prodik/application/authorization/refresh.py delete mode 100644 src/prodik/application/authorization/register.py delete mode 100644 src/prodik/application/interfaces/repositories/authorization/__init__.py delete mode 100644 src/prodik/application/interfaces/repositories/authorization/local_authorization.py delete mode 100644 src/prodik/application/interfaces/repositories/authorization/session.py delete mode 100644 src/prodik/application/interfaces/token_managers/__init__.py delete mode 100644 src/prodik/application/interfaces/token_managers/access_token_manager.py delete mode 100644 src/prodik/application/interfaces/token_managers/refresh_token_manager.py delete mode 100644 src/prodik/application/manage_credentials/__init__.py delete mode 100644 src/prodik/application/manage_credentials/update_current_user_password.py delete mode 100644 src/prodik/bootstrap/di/providers/cache.py delete mode 100644 src/prodik/domain/authorization/__init__.py delete mode 100644 src/prodik/domain/authorization/local/__init__.py delete mode 100644 src/prodik/domain/authorization/local/model.py delete mode 100644 src/prodik/domain/authorization/oauth/__init__.py delete mode 100644 src/prodik/domain/authorization/oauth/model.py delete mode 100644 src/prodik/domain/authorization/session/__init__.py delete mode 100644 src/prodik/domain/authorization/session/model.py delete mode 100644 src/prodik/infrastructure/identity_provider.py delete mode 100644 src/prodik/infrastructure/repositories/authorization/__init__.py delete mode 100644 src/prodik/infrastructure/repositories/authorization/local_authorization.py delete mode 100644 src/prodik/infrastructure/repositories/authorization/session.py delete mode 100644 src/prodik/infrastructure/token_managers/__init__.py delete mode 100644 src/prodik/infrastructure/token_managers/access_token_manager.py delete mode 100644 src/prodik/infrastructure/token_managers/refresh_token_manager.py delete mode 100644 src/prodik/presentation/schemas/auth.py delete mode 100644 src/prodik/presentation/views/auth.py delete mode 100644 tests/e2e/authentication/__init__.py delete mode 100644 tests/e2e/authentication/test_login.py delete mode 100644 tests/e2e/authentication/test_refresh_token.py delete mode 100644 tests/e2e/authentication/test_register.py delete mode 100644 tests/factories/schemas/authentication.py diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8462e75..1000f6b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,38 +1,38 @@ -name: Python CI +name: Checks on: push: - branches: ["dev", "master"] + branches: + - master + - dev pull_request: - branches: ["dev", "master"] + branches: + - master + - dev permissions: contents: read jobs: - build: + lint: + name: Lint & type-check (Python ${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - python-version: ["3.12", "3.13"] + python-version: ["3.13"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false - - name: Set up Python - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: python-version: ${{ matrix.python-version }} - cache: "pip" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install .[dev] - - - name: Ruff - run: ruff check . - - name: Mypy - run: mypy . + - run: uv sync --group dev + - run: uv run ruff check src + - run: uv run ruff format --check src + - run: uv run mypy . --strict \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b5bf309..1654041 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM python:3.13-slim-bookworm AS python-base ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PIP_NO_CACHE_DIR=1 \ - APP_PATH=/prodik \ + APP_PATH=/conode \ UV_VERSION=0.9.22 ENV VIRTUAL_ENV="$APP_PATH/.venv" diff --git a/README.md b/README.md index 6b25965..f5e6450 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -Fun, useless (maybe), interactive project +_+ +Fun, useless (maybe), interactive project +_+ diff --git a/deploy/init.sql b/deploy/init.sql new file mode 100644 index 0000000..06aabe8 --- /dev/null +++ b/deploy/init.sql @@ -0,0 +1,27 @@ +-- Initialize user and database for KeyCloak + +CREATE ROLE keycloak +WITH + LOGIN + PASSWORD 'keycloakPassword'; + +CREATE DATABASE keycloak + OWNER keycloak + ENCODING 'UTF8' + TEMPLATE template0; + +\connect keycloak; + +GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak; + +GRANT ALL ON SCHEMA public TO keycloak; +ALTER SCHEMA public OWNER TO keycloak; + +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT ALL ON TABLES TO keycloak; + +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT ALL ON SEQUENCES TO keycloak; + +ALTER DEFAULT PRIVILEGES IN SCHEMA public +GRANT ALL ON FUNCTIONS TO keycloak; \ No newline at end of file diff --git a/deploy/keycloak.env b/deploy/keycloak.env new file mode 100644 index 0000000..0405a4e --- /dev/null +++ b/deploy/keycloak.env @@ -0,0 +1,8 @@ +KC_DB=postgres +KC_DB_USERNAME=keycloak +KC_DB_URL_HOST=postgres +KC_DB_URL_PORT=5432 +KC_BOOTSTRAP_ADMIN_USERNAME=keycloak +KC_BOOTSTRAP_ADMIN_PASSWORD=keycloakPassword +KC_DB_PASSWORD=keycloakPassword +KC_HEALTH_ENABLED=true \ No newline at end of file diff --git a/deploy/keycloak.json b/deploy/keycloak.json new file mode 100644 index 0000000..be218da --- /dev/null +++ b/deploy/keycloak.json @@ -0,0 +1,3099 @@ +{ + "id": "ec41a80a-173f-4609-8a8d-b1fac4205041", + "realm": "conode", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": true, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "bruteForceStrategy": "MULTIPLE", + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "maxSecondaryAuthFailures": 0, + "roles": { + "realm": [ + { + "id": "49a995af-a1d7-42ab-8a43-966a78ef4331", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "ec41a80a-173f-4609-8a8d-b1fac4205041", + "attributes": {} + }, + { + "id": "ddce9f6a-35d1-410d-8024-f256234f531a", + "name": "admin", + "description": "", + "composite": false, + "clientRole": false, + "containerId": "ec41a80a-173f-4609-8a8d-b1fac4205041", + "attributes": {} + }, + { + "id": "2bebf11f-cdf1-4aff-b428-40430e728bd4", + "name": "default-roles-conode", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "realm-management": [ + "view-realm", + "query-users", + "impersonation", + "query-organizations", + "query-clients", + "view-clients", + "view-events", + "view-users", + "query-groups", + "view-authorization", + "query-realms" + ], + "broker": [ + "read-token" + ], + "account": [ + "delete-account", + "view-groups", + "view-applications", + "manage-account-links", + "view-consent", + "manage-consent", + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "ec41a80a-173f-4609-8a8d-b1fac4205041", + "attributes": {} + }, + { + "id": "e1b0dbeb-0764-45fc-956a-a12aebaf0f14", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "ec41a80a-173f-4609-8a8d-b1fac4205041", + "attributes": {} + } + ], + "client": { + "conode": [ + { + "id": "1f2189b1-ec0e-4a51-a496-5ccf64330044", + "name": "uma_protection", + "composite": false, + "clientRole": true, + "containerId": "4a49a878-bf46-409c-881c-16896bdef462", + "attributes": {} + } + ], + "realm-management": [ + { + "id": "13d780d4-44c5-433d-b26d-719ef5fb695f", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "90dca780-0937-40f3-a533-12abf29e90c9", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "0c867e97-e15c-4a34-b012-f07e8b996564", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "91c01681-77c9-4b12-ada0-8b3c4d104d16", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "e315a1f6-4ac0-4d06-90e5-31616801ec9e", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "f086cb99-afdc-4724-af7d-e9d3ff5e6818", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "67209571-2887-486f-8cba-146fb12ddd10", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "635a48ad-43d7-4b4a-b6a3-1c747297fc3b", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "b883cdae-9702-4df2-a8bf-d48302462a92", + "name": "query-organizations", + "description": "${role_query-organizations}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "e5a35f08-7dc7-437d-8635-dff0a0cdbdc9", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "26429b17-4e42-4d7c-a35b-1ec74951ec41", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-realm", + "view-realm", + "manage-identity-providers", + "query-users", + "create-client", + "manage-events", + "view-identity-providers", + "impersonation", + "query-organizations", + "manage-authorization", + "manage-users", + "query-clients", + "view-clients", + "manage-organizations", + "publish-events", + "view-events", + "view-users", + "query-groups", + "view-organizations", + "view-authorization", + "manage-clients", + "query-realms" + ] + } + }, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "3b908b30-b228-472a-8393-65777261f8a1", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "de68d8b9-f696-4862-87a2-fab2cdf4e88b", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "11306d4c-6f0b-4d6f-a3ed-b3c82997678e", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "b6519e23-766c-4351-b1ac-a4a2e3eb0f21", + "name": "manage-organizations", + "description": "${role_manage-organizations}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "a2413153-b6ec-472b-9daa-57d90bcbf0c5", + "name": "publish-events", + "description": "${role_publish-events}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "71fa910a-68c0-4aa6-b24e-fa08b1647257", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "e1092238-6117-4b18-81b9-3d147478c3af", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "da967960-8b1c-4aa4-bad6-a9575b285263", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "5319760f-a94e-47e6-8a64-bd3be72b5e9f", + "name": "view-organizations", + "description": "${role_view-organizations}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-organizations" + ] + } + }, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "51552ffa-14c0-46d1-9fdc-0dba0687e2bc", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "f4bb4418-7109-4d9b-b681-6130819d30d7", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "c517c084-2b9e-4787-9946-afc1d039b3a9", + "name": "create-organization", + "description": "${role_create-organization}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + }, + { + "id": "07726516-72a9-4397-9c96-c257bc763963", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "2b6ee18a-5dd5-43f4-8c2d-1209a645dae4", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "7904be1a-7d78-4b10-ac95-486e25689078", + "attributes": {} + } + ], + "account": [ + { + "id": "d1ba3ac8-71fc-4ce6-a8dc-f0fc8b709fe0", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "a7929188-ce65-40b3-9ca1-05bc39659e81", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "b737ec04-5145-434a-a952-67e277e5dfc2", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "29dd0736-9496-4eb4-9046-2a85ddfeced5", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "e0723b7a-8ead-4332-ba22-dd048278b678", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "f6b3caf7-b6b6-452b-8466-e5c3cebe475e", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "f874a5b8-3b8d-493d-a347-09ddb1a7658a", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + }, + { + "id": "c79656f9-016f-4a7f-a068-e4033cdabb0f", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "2bebf11f-cdf1-4aff-b428-40430e728bd4", + "name": "default-roles-conode", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "ec41a80a-173f-4609-8a8d-b1fac4205041" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256", + "RS256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256", + "RS256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessResidentKey": "required", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "required", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "2378b129-4f28-4cd9-bc25-24832d3c8b4f", + "username": "service-account-conode", + "emailVerified": false, + "enabled": true, + "createdTimestamp": 1784228290542, + "totp": false, + "serviceAccountClientId": "conode", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-conode" + ], + "clientRoles": { + "conode": [ + "uma_protection" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account", + "view-groups" + ] + } + ] + }, + "clients": [ + { + "id": "75942b65-13ab-4635-9368-bdc005bd6bfa", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/conode/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/conode/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + }, + { + "id": "132eb0b9-145b-4141-92d9-c7595b7ae31c", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/conode/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/conode/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "b87ac1f1-ca34-485d-a477-de0f5648315d", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + }, + { + "id": "e0bf2bae-7450-47d4-940c-a25a5d7a9088", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + }, + { + "id": "7904be1a-7d78-4b10-ac95-486e25689078", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + }, + { + "id": "4a49a878-bf46-409c-881c-16896bdef462", + "clientId": "conode", + "name": "Conode", + "description": "Easy-to-use API for building graphs with state contexts. ", + "rootUrl": "http://conode.com", + "adminUrl": "http://conode.com", + "baseUrl": "http://conode.com", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "http://api.conode.com/oauth2/callback" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "authorizationServicesEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "logout.confirmation.enabled": "false", + "client.secret.creation.time": "1784228290", + "oauth2.jwt.authorization.grant.enabled": "false", + "standard.token.exchange.enabled": "false", + "frontchannel.logout.session.required": "true", + "oauth2.device.authorization.grant.enabled": "false", + "use.jwks.url": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "realm_client": "false", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "display.on.consent.screen": "false", + "dpop.bound.access.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "4ab86e46-3496-496f-8bbc-fdeff0c12a79", + "name": "aud-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "conode", + "id.token.claim": "false", + "lightweight.claim": "false", + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ], + "defaultClientScopes": [ + "service_account", + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ], + "authorizationSettings": { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [], + "policies": [], + "scopes": [], + "decisionStrategy": "UNANIMOUS" + } + }, + { + "id": "e17b8210-42bb-4c4e-9de1-cf71c737dee9", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + }, + { + "id": "81c0c5f2-8a07-43f8-af8c-648586a203ef", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/conode/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/conode/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "531e9c28-1c10-455b-940e-911a0a0f6419", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "organization", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "8bbf89b4-7236-4ff2-89a5-3728c2b38c4b", + "name": "service_account", + "description": "Specific scope for a client enabled for service accounts", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "c2165ab9-929b-4131-8f0f-6862683241e5", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + }, + { + "id": "45f94f95-1a50-470e-8fd7-c798e1e961fa", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "id": "fc1e2541-7286-4f4d-ada6-553be302a8e8", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "8a8108cf-eba7-46d6-a4ae-271a2c01388f", + "name": "organization", + "description": "Additional claims about the organization a subject belongs to", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${organizationScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "6465ac88-a49b-4a28-93ca-5eb8f07e2fbc", + "name": "organization", + "protocol": "openid-connect", + "protocolMapper": "oidc-organization-membership-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "organization", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "0acd2827-2a80-4226-92ab-72f86772aff2", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "6e8f2433-ee66-4d36-a7b3-1bcd0dcd9d29", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "258353df-f076-4824-af06-416c48faefb5", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "5fbfc1d0-03bf-4339-9cb7-af7ca7e73969", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "85448af4-c41c-42f6-ac45-58226850bb14", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "436794f4-1168-45e8-85a9-51d690d424fb", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "42e3cd61-aed5-4a4d-9b8b-eebdad37a805", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "959a0468-8688-4349-b473-7ca1f962c22d", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "cb285e2b-ac4e-4905-8654-4b242a4f0bfd", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "281dd374-e131-460a-8efd-f448d563b883", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "fbb0888c-19e3-4b8d-ab2f-8ed61e664f1d", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "05b46a46-fde1-4e2e-ad4d-5aff6e803f25", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "76085687-cc5a-4a25-a8c6-063d67945dda", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "b9ef9e68-c26a-4d2a-90f9-e0b201340c37", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "b41e6db7-a840-423d-a8c7-234bfa35d75a", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "43fee56e-094a-4a3b-98da-9185b91a584b", + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "5a6fe513-023e-4ad0-82be-2be4765c3468", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "d17da110-609e-47b9-8f38-f79f8be45181", + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + } + ] + }, + { + "id": "e9bd8ed1-554b-4c77-a225-a564b92d5fcb", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "1175d01e-62d0-48d1-bd1f-af409b1ec65b", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "0059d0b3-3f15-4525-a78e-4472842ad865", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "b52a9b4a-b171-4be1-9509-61e6607a2e25", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "307c203d-7474-4833-b883-e5c02ab5ab97", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "0f468a65-1754-4be9-8105-1e6bb1b5c55f", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "2839fee0-de0d-4c6c-9328-868e16c45d92", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "f0c6a48e-ad11-40d1-9714-311fa79e7fce", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a4f804a0-f845-4672-b1e4-35b048bb27d1", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "9869edaf-21a9-4dec-a5d9-13b7a565136b", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "fc63864e-3391-4ce0-b908-0647119a3a8b", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "3a020067-bd91-4127-963f-50bc2fd4b05a", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "99310df5-6af0-4a2e-aa9e-cbd6992d259d", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "31490237-0398-41b3-a1ed-dcc69a7ef1fc", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "ea53e139-b0ec-4f61-bd8f-5a974ba66da5", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "5f5cdb3b-ec94-43ae-ab80-b5ca8acceb82", + "name": "saml_organization", + "description": "Organization Membership", + "protocol": "saml", + "attributes": { + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "d307f0d4-e1b3-493d-bf51-d721b90b6cd8", + "name": "organization", + "protocol": "saml", + "protocolMapper": "saml-organization-membership-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "d70b8ad4-82c1-4567-9b11-5eb880489ebc", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "7f6a6f9e-34c4-4ff3-b4bb-c342f10f3651", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "ad635e8a-73f2-40c8-bc4e-44ed2baac881", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "e0feb8b3-0035-449c-be17-ae5140fbb675", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "af02940a-fe65-4d2b-9028-e5a75ee3182f", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "39163d5a-c8d0-4edf-8388-194be5134053", + "name": "AuthnContextClassRef", + "description": "AuthnContextClassRef Level of Authentiation", + "protocol": "saml", + "attributes": {}, + "protocolMappers": [ + { + "id": "d91a12f6-3f8c-4cce-b411-8c66768277b4", + "name": "AuthnContextClassRef", + "protocol": "saml", + "protocolMapper": "saml-authn-context-class-ref-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "7ab00999-4f7b-4b3e-a139-3ba33692b9f5", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "ef48ad34-d63e-440a-b142-afea9a37e4cb", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "saml_organization", + "AuthnContextClassRef", + "profile", + "email", + "roles", + "web-origins", + "acr", + "basic" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt", + "organization" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "loginTheme": "phasetwo-ui", + "accountTheme": "phasetwo-ui", + "adminTheme": "phasetwo-ui", + "emailTheme": "phasetwo-ui", + "eventsEnabled": true, + "eventsListeners": [ + "ext-event-http", + "jboss-logging", + "ext-event-script", + "ext-event-webhook" + ], + "enabledEventTypes": [ + "UPDATE_CONSENT_ERROR", + "UPDATE_TOTP", + "UPDATE_CREDENTIAL", + "IMPERSONATE_ERROR", + "CUSTOM_REQUIRED_ACTION", + "RESTART_AUTHENTICATION", + "VERIFIABLE_CREDENTIAL_OFFER_REQUEST", + "IMPERSONATE", + "LOGIN", + "CLIENT_INITIATED_ACCOUNT_LINKING", + "OAUTH2_EXTENSION_GRANT", + "USER_DISABLED_BY_PERMANENT_LOCKOUT", + "VERIFIABLE_CREDENTIAL_REQUEST", + "REMOVE_CREDENTIAL_ERROR", + "TOKEN_EXCHANGE", + "REGISTER", + "DELETE_ACCOUNT_ERROR", + "VERIFIABLE_CREDENTIAL_OFFER_REQUEST_ERROR", + "IDENTITY_PROVIDER_LINK_ACCOUNT", + "USER_DISABLED_BY_TEMPORARY_LOCKOUT", + "DELETE_ACCOUNT", + "UPDATE_PASSWORD", + "IDENTITY_PROVIDER_FIRST_LOGIN", + "VERIFY_EMAIL", + "CLIENT_LOGIN_ERROR", + "RESTART_AUTHENTICATION_ERROR", + "EXECUTE_ACTIONS", + "REMOVE_FEDERATED_IDENTITY_ERROR", + "TOKEN_EXCHANGE_ERROR", + "VERIFIABLE_CREDENTIAL_PRE_AUTHORIZED_GRANT", + "FEDERATED_IDENTITY_OVERRIDE_LINK", + "SEND_IDENTITY_PROVIDER_LINK_ERROR", + "VERIFIABLE_CREDENTIAL_CREATE_OFFER", + "JWT_AUTHORIZATION_GRANT_ERROR", + "VERIFIABLE_CREDENTIAL_PRE_AUTHORIZED_GRANT_ERROR", + "EXECUTE_ACTION_TOKEN_ERROR", + "OAUTH2_EXTENSION_GRANT_ERROR", + "SEND_VERIFY_EMAIL", + "EXECUTE_ACTIONS_ERROR", + "OAUTH2_DEVICE_CODE_TO_TOKEN", + "OAUTH2_DEVICE_VERIFY_USER_CODE_ERROR", + "REVOKE_GRANT_ERROR", + "UPDATE_EMAIL_ERROR", + "INVITE_ORG_ERROR", + "UPDATE_PROFILE", + "VERIFIABLE_CREDENTIAL_REQUEST_ERROR", + "SEND_IDENTITY_PROVIDER_LINK", + "SEND_VERIFY_EMAIL_ERROR", + "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", + "OAUTH2_DEVICE_AUTH_ERROR", + "REMOVE_TOTP_ERROR", + "VERIFY_EMAIL_ERROR", + "CLIENT_UPDATE", + "JWT_AUTHORIZATION_GRANT", + "UPDATE_TOTP_ERROR", + "VERIFY_PROFILE", + "GRANT_CONSENT_ERROR", + "SEND_RESET_PASSWORD", + "GRANT_CONSENT", + "VERIFY_PROFILE_ERROR", + "REMOVE_TOTP", + "REVOKE_GRANT", + "LOGIN_ERROR", + "CLIENT_LOGIN", + "RESET_PASSWORD_ERROR", + "CODE_TO_TOKEN_ERROR", + "OAUTH2_DEVICE_CODE_TO_TOKEN_ERROR", + "UPDATE_PROFILE_ERROR", + "OAUTH2_DEVICE_VERIFY_USER_CODE", + "UPDATE_PASSWORD_ERROR", + "IDENTITY_PROVIDER_LOGIN", + "AUTHREQID_TO_TOKEN", + "LOGOUT", + "CLIENT_REGISTER", + "CLIENT_DELETE", + "FEDERATED_IDENTITY_LINK_ERROR", + "CLIENT_DELETE_ERROR", + "PERMISSION_TOKEN", + "REGISTER_NODE", + "UPDATE_CREDENTIAL_ERROR", + "OAUTH2_DEVICE_AUTH", + "REMOVE_FEDERATED_IDENTITY", + "IDENTITY_PROVIDER_POST_LOGIN", + "VERIFIABLE_CREDENTIAL_CREATE_OFFER_ERROR", + "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", + "FEDERATED_IDENTITY_OVERRIDE_LINK_ERROR", + "UPDATE_EMAIL", + "REGISTER_ERROR", + "EXECUTE_ACTION_TOKEN", + "LOGOUT_ERROR", + "CLIENT_UPDATE_ERROR", + "AUTHREQID_TO_TOKEN_ERROR", + "CLIENT_REGISTER_ERROR", + "FEDERATED_IDENTITY_LINK", + "INVITE_ORG", + "IDENTITY_PROVIDER_LOGIN_ERROR", + "RESET_PASSWORD", + "REMOVE_CREDENTIAL", + "UPDATE_CONSENT", + "SEND_RESET_PASSWORD_ERROR", + "CUSTOM_REQUIRED_ACTION_ERROR", + "IDENTITY_PROVIDER_POST_LOGIN_ERROR", + "CODE_TO_TOKEN", + "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR" + ], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "f9b59027-c19f-439f-8734-1c59de02ed1d", + "name": "Allowed Registration Web Origins", + "providerId": "registration-web-origins", + "subType": "authenticated", + "subComponents": {}, + "config": {} + }, + { + "id": "147d6e99-6208-49e9-986f-aecc17906db7", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper" + ] + } + }, + { + "id": "e97ff134-c0ad-4691-afce-88082ab14386", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "048701e6-eeb1-44c3-9fa4-109df2d9cc55", + "name": "Allowed Registration Web Origins", + "providerId": "registration-web-origins", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "080f41d8-32ec-41ce-a632-f03f3081d665", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "513d33d8-77d7-4991-b4df-06058e4b7c96", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "04f87943-c844-42c0-85dc-04d90772d8c9", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper", + "oidc-address-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "216a7ba0-35c6-4836-9d9a-99879a74b9ae", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "b5431799-d570-4cd3-bf2a-baddaca33044", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "8c27fcaf-74ba-4622-a1b8-9f6676297dda", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "a5f87151-1431-4d46-a906-32f6354fa33a", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"org.ro.active\",\"displayName\":\"Active organization ID\",\"permissions\":{\"view\":[\"admin\"],\"edit\":[\"admin\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}]}" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "e131821d-c569-4ed8-8b5b-5ad84fcc0c13", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "5e14e592-fe10-4f38-bc98-1f12e3d05b83", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "fa40d5cf-3027-40b3-819d-a1a1eae49129", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "a5dfc729-180d-4bfc-839b-92c96b37f6e0", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS512" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "authenticationFlows": [ + { + "id": "2c01c801-7adf-4dc4-b002-d7053cd7f207", + "alias": "Account verification options", + "description": "Method with which to verify the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "ce8fefa6-a08c-4ef6-b7ae-23114f8f4c70", + "alias": "Browser - Conditional 2FA", + "description": "Flow to determine if any 2FA is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorConfig": "browser-conditional-credential", + "authenticator": "conditional-credential", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "webauthn-authenticator", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-recovery-authn-code-form", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "5f39626d-9db7-4f77-bd5f-79a53d4bc027", + "alias": "Browser - Conditional Organization", + "description": "Flow to determine if the organization identity-first login is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "organization", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8603ddbd-7da4-409e-9a48-39ad4d663da2", + "alias": "Cookies Sub-Flow", + "description": "Cookie sub-flow which can be used to switch org.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "ext-select-org", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "e445c393-f9a0-405e-b7fb-08c7e7060338", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "5fb98117-9ddf-415b-a541-b84a2879fdd0", + "alias": "First Broker Login - Conditional Organization", + "description": "Flow to determine if the authenticator that adds organization members is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "idp-add-organization-member", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6e11765b-49f7-4896-a911-9fc91414f7e4", + "alias": "First broker login - Conditional 2FA", + "description": "Flow to determine if any 2FA is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorConfig": "first-broker-login-conditional-credential", + "authenticator": "conditional-credential", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "webauthn-authenticator", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-recovery-authn-code-form", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8d0f87a9-64c0-4eee-815c-1006a46265e5", + "alias": "Forms Sub-Flow", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Org Browser - Conditional OTP", + "userSetupAllowed": false + }, + { + "authenticator": "ext-select-org", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "e3b30e27-cd94-498f-9444-7e88f1868f6c", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "164311b5-d365-4029-a197-7ffd969854ee", + "alias": "IDP Sub-Flow", + "description": "IDP sub-flow to select org.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "ext-select-org", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "ef77b92d-1007-4c1c-ad31-ff15c74fb10b", + "alias": "Org Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "814fe89f-9d8d-4d69-bee5-73819e59b067", + "alias": "Org Browser Flow", + "description": "Browser flow with select organization step.", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "Cookies Sub-Flow", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "IDP Sub-Flow", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Forms Sub-Flow", + "userSetupAllowed": false + } + ] + }, + { + "id": "8474c11c-3ae9-4166-9259-7774c4ac1630", + "alias": "Org Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "665e9934-860a-4c85-bf4f-fd7811456702", + "alias": "Org Direct Grant Flow", + "description": "Direct grant flow with select organization step.", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Org Direct Grant - Conditional OTP", + "userSetupAllowed": false + }, + { + "authenticator": "ext-select-org", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "f3476a0f-c796-40cb-beba-716f5542fab8", + "alias": "Organization", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "9984e4ed-07e6-4088-b62f-9c78dcc9bb04", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b61fc983-ae44-4110-b60b-efaa6b0e091e", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "f1ce0698-6ed6-4374-a834-9d31eb17d489", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional 2FA", + "userSetupAllowed": false + } + ] + }, + { + "id": "acced864-c2c9-484f-8e90-639f2994731b", + "alias": "browser", + "description": "Browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 26, + "autheticatorFlow": true, + "flowAlias": "Organization", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "519f6224-da61-49f6-9a42-5e7a9b766118", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "federated-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8f53d8c1-379c-4ec6-96a0-0c2ec168c8f8", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "fa5e8e61-7d82-4dc3-ac8e-a9d3b5e630d3", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "a923c89d-fa04-4560-8587-1b1e6f12a496", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 60, + "autheticatorFlow": true, + "flowAlias": "First Broker Login - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "ac4b0ce5-bd3e-4f8a-8db2-2a3c5f5ef46e", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional 2FA", + "userSetupAllowed": false + } + ] + }, + { + "id": "1a27d5cc-da95-469f-b78e-d535bb9d4713", + "alias": "registration", + "description": "Registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "b020c213-18ae-4812-bc27-961db8667346", + "alias": "registration form", + "description": "Registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-terms-and-conditions", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 70, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "a2f440c0-dc4b-486a-8299-12909c7cd71a", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "ef4201b1-d166-47c3-8fdc-c254b11d32b1", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "b87b8c69-9a49-4f42-a1e6-41c259d3de38", + "alias": "browser-conditional-credential", + "config": { + "credentials": "webauthn-passwordless" + } + }, + { + "id": "33a0b22e-9103-44ac-8813-48a596f6b021", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "a26f5110-d2c5-4235-a0ad-c40185a64599", + "alias": "first-broker-login-conditional-credential", + "config": { + "credentials": "webauthn-passwordless" + } + }, + { + "id": "4d1e7d2f-cc4f-4d03-a639-c0b61c58f478", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 54, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 57, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "UPDATE_EMAIL", + "name": "Update Email", + "providerId": "UPDATE_EMAIL", + "enabled": false, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 90, + "config": {} + }, + { + "alias": "VERIFY_PROFILE", + "name": "Verify Profile", + "providerId": "VERIFY_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 100, + "config": {} + }, + { + "alias": "delete_credential", + "name": "Delete Credential", + "providerId": "delete_credential", + "enabled": true, + "defaultAction": false, + "priority": 110, + "config": {} + }, + { + "alias": "idp_link", + "name": "Linking Identity Provider", + "providerId": "idp_link", + "enabled": true, + "defaultAction": false, + "priority": 120, + "config": {} + }, + { + "alias": "CONFIGURE_RECOVERY_AUTHN_CODES", + "name": "Recovery Authentication Codes", + "providerId": "CONFIGURE_RECOVERY_AUTHN_CODES", + "enabled": true, + "defaultAction": false, + "priority": 130, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DevicePollingInterval": "5", + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "scimApiEnabled": "false", + "darkMode": "true" + }, + "keycloakVersion": "26.7.0", + "userManagedAccessAllowed": false, + "organizationsEnabled": false, + "verifiableCredentialsEnabled": false, + "adminPermissionsEnabled": false, + "scimApiEnabled": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} \ No newline at end of file diff --git a/deploy/nginx.conf b/deploy/nginx.conf deleted file mode 100644 index f442545..0000000 --- a/deploy/nginx.conf +++ /dev/null @@ -1,15 +0,0 @@ -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - - server { - listen 80; - - location / { - proxy_pass http://api:8000; - } - } -} diff --git a/deploy/oauth_proxy.toml b/deploy/oauth_proxy.toml new file mode 100644 index 0000000..8acbeb7 --- /dev/null +++ b/deploy/oauth_proxy.toml @@ -0,0 +1,25 @@ +upstreams=["http://api:8000"] +http_address="0.0.0.0:4180" +reverse_proxy=true +cookie_httponly=true +request_logging=true +auth_logging=true +standard_logging=true +skip_provider_button=true +set_xauthrequest=true +pass_authorization_header=true +set_authorization_header=true +pass_access_token=true +insecure_oidc_allow_unverified_email=true +provider="oidc" +redirect_url="http://api.conode.com/oauth2/callback" +oidc_issuer_url="http://keycloak.conode.com/realms/conode" +scope="openid email profile offline_access" +code_challenge_method="S256" +email_domains="*" +cookie_domains=[".conode.com"] +whitelist_domains=[".conode.com"] +cookie_refresh="0h4m0s" +cookie_expire="720h0m0s" +cookie_secure=false +cookie_csrf_per_request = true \ No newline at end of file diff --git a/deploy/postgres.env b/deploy/postgres.env index f14be83..28dc540 100644 --- a/deploy/postgres.env +++ b/deploy/postgres.env @@ -1,7 +1,5 @@ -POSTGRES_DB=postgres -POSTGRES_USER=postgres -POSTGRES_PASSWORD=admin_password -POSTGRES_HOST=postgres -POSTGRES_PORT=5432 +POSTGRES_DB=conode +POSTGRES_USER=conode +POSTGRES_PASSWORD=conodeSecretPassword LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 diff --git a/docker-compose.base.yaml b/docker-compose.base.yaml index bc3787f..51c6adc 100644 --- a/docker-compose.base.yaml +++ b/docker-compose.base.yaml @@ -1,40 +1,139 @@ services: api: build: . + restart: on-failure - container_name: api - command: ["prodik", "api", "run"] - ports: - - 8000:8000 + command: ["conode", "api", "run"] + + depends_on: + keycloak: + condition: service_healthy + postgres: + condition: service_healthy + migrations: + condition: service_completed_successfully + + networks: + - conode-network + + migrations: + build: . + + restart: on-failure + command: ["conode", "migrations", "upgrade"] + depends_on: postgres: condition: service_healthy + + networks: + - conode-network + + oauth: + image: quay.io/oauth2-proxy/oauth2-proxy:latest + command: ["--config=/etc/oauth2-proxy/oauth2-proxy.toml"] + + environment: + OAUTH2_PROXY_CLIENT_ID: conode + OAUTH2_PROXY_CLIENT_SECRET: HxZkeEWaKX0J1rXNPpkshkhojgm6roqrNxWWtTBtUiwSm4kN1QzIH1cCcTulKPlYqCEFVELVuFu94tqFGDyjuo + OAUTH2_PROXY_COOKIE_SECRET: x3CpXUQxfDgXzDDxIdST3mTbTDosKOlx + + volumes: + - "./deploy/oauth_proxy.toml:/etc/oauth2-proxy/oauth2-proxy.toml" + + labels: + - "traefik.enable=true" + + - "traefik.http.routers.oauth.rule=Host(`api.conode.com`)" + - "traefik.http.routers.oauth.entrypoints=web" + - "traefik.http.services.oauth.loadbalancer.server.port=4180" + networks: - - prodik-network + - conode-network + + depends_on: + keycloak: + condition: service_healthy + + traefik: + image: traefik:latest + command: + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + ports: + - "80:80" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" - redis: - image: redis:latest networks: - prodik-network: + conode-network: aliases: - - cache.literally.io + - keycloak.conode.com + - api.conode.com + + keycloak: + image: quay.io/keycloak/keycloak:latest + + command: start-dev --import-realm + + depends_on: + postgres: + condition: service_healthy + + env_file: + - "./deploy/keycloak.env" + + healthcheck: + test: + - CMD-SHELL + - > + { printf 'HEAD /health/ready HTTP/1.0\r\n\r\n' >&0; + grep -q '200'; } 0<>/dev/tcp/localhost/9000 + interval: 5s + timeout: 10s + retries: 30 + start_period: 30s + + labels: + - "traefik.enable=true" + - "traefik.http.routers.keycloak.rule=Host(`keycloak.conode.com`)" + - "traefik.http.routers.keycloak.entrypoints=web" + - "traefik.http.services.keycloak.loadbalancer.server.port=8080" + + volumes: + - keycloak-data:/opt/keycloak/ + - ./deploy/keycloak.json:/opt/keycloak/data/import/realm-export.json + + networks: + - conode-network postgres: image: postgres:16-alpine + volumes: - postgres-data:/var/lib/postgresql/data - ports: - - 5432:5432 + - ./deploy:/docker-entrypoint-initdb.d:ro + networks: - prodik-network: + conode-network: aliases: - persistence.literally.io + env_file: - "./deploy/postgres.env" + healthcheck: + test: ["CMD", "pg_isready", "-U", "conode", "-d", "conode"] + interval: 5s + timeout: 15s + retries: 5 + volumes: postgres-data: + keycloak-data: networks: - prodik-network: + conode-network: driver: bridge \ No newline at end of file diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 59d92f0..8383f94 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -3,15 +3,19 @@ services: environment: DEBUG: "true" volumes: - - "./src/prodik/:/prodik/src/prodik/" + - "./src/conode/:/conode/src/conode/" - redis: + postgres: ports: - - 6379:6379 + - 5432:5432 - postgres: - healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"] - interval: 5s - timeout: 5s - retries: 5 \ No newline at end of file + traefik: + command: + - "--log.level=DEBUG" + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + ports: + - "80:80" + - "8080:8080" \ No newline at end of file diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index f3563bb..bdad469 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -3,11 +3,4 @@ services: environment: DEBUG: "false" volumes: - - "./src/prodik/infrastructure/db/migrations/versions/:/prodik/src/prodik/infrastructure/db/migrations/versions/" - - postgres: - healthcheck: - test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"] - interval: 15s - timeout: 15s - retries: 5 \ No newline at end of file + - "./src/conode/infrastructure/db/migrations/versions/:/conode/src/conode/infrastructure/db/migrations/versions/" \ No newline at end of file diff --git a/example.config.toml b/example.config.toml index 7ec4eaa..6ed3150 100644 --- a/example.config.toml +++ b/example.config.toml @@ -9,11 +9,9 @@ host = "persistence.literally.io" database = "postgres" port = 5432 -[cache] -host = "cache.literally.io" -port = 6379 -ttl = 3600 - [secrets] secret = "ABC123" -expires_in = 3600 \ No newline at end of file +expires_in = 3600 +jwks_url = "http://keycloak:8080/realms/conode/protocol/openid-connect/certs" +issuer = "http://keycloak.conode.com/realms/conode" +audience = "conode" diff --git a/pyproject.toml b/pyproject.toml index f060acc..7986439 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "prodik" +name = "Conode" version = "0.1.0" description = "Great & powerful application" readme = "README.md" @@ -13,30 +13,32 @@ dependencies = [ "fastapi==0.128.0", "pydantic[email]==2.13.2", "pyjwt==2.11.0", - "redis[hiredis]==7.4.0", "sqlalchemy==2.0.46", "structlog==25.5.0", "uvicorn==0.40.0", ] -[project.optional-dependencies] +[dependency-groups] dev = [ "polyfactory==3.3.0", + "bandit==1.9.4", "dirty-equals==0.11", "httpx==0.28.1", "pytest-asyncio==1.3.0", "pytest", "mypy", + "semgrep==0.112.1", "ruff", - "pytest-cov>=7.1.0", - "pytest-xdist>=3.8.0", + "pytest-cov==7.1.0", + "pytest-xdist==3.8.0", + "zizmor==1.27.0", "datamodel-code-generator==0.54.0", ] [tool.ruff] target-version = "py313" line-length = 88 -exclude = [".venv", "src/prodik/infrastructure/persistence/migrations"] +exclude = [".venv", "src/conode/infrastructure/persistence/migrations"] [tool.ruff.lint] extend-select = ["ALL"] @@ -74,7 +76,7 @@ addopts = "--cov-report=term-missing" [tool.mypy] strict = true explicit_package_bases = true -exclude = ["build", "src/prodik/infrastructure/persistence/migrations"] +exclude = ["build", "src/conode/infrastructure/persistence/migrations"] warn_unreachable = true show_column_numbers = true show_error_context = true @@ -86,8 +88,8 @@ mypy_path = "src" plugins = [ "pydantic.mypy", ] -files = ["src/prodik"] +files = ["src/conode"] [project.scripts] -prodik = "prodik.bootstrap.cli:main" +conode = "conode.bootstrap.cli:main" diff --git a/src/prodik/application/__init__.py b/src/conode/application/__init__.py similarity index 100% rename from src/prodik/application/__init__.py rename to src/conode/application/__init__.py diff --git a/src/prodik/application/attach_node_to_group/__init__.py b/src/conode/application/attach_node_to_group/__init__.py similarity index 100% rename from src/prodik/application/attach_node_to_group/__init__.py rename to src/conode/application/attach_node_to_group/__init__.py diff --git a/src/prodik/application/attach_node_to_group/attach_node.py b/src/conode/application/attach_node_to_group/attach_node.py similarity index 85% rename from src/prodik/application/attach_node_to_group/attach_node.py rename to src/conode/application/attach_node_to_group/attach_node.py index 4c8c213..5cbbae3 100644 --- a/src/prodik/application/attach_node_to_group/attach_node.py +++ b/src/conode/application/attach_node_to_group/attach_node.py @@ -1,19 +1,19 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.errors import ( +from conode.application.errors import ( NodeNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.group import GroupId -from prodik.domain.node import NodeAssociation, NodeAssociationId, NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.group import GroupId +from conode.domain.node import NodeAssociation, NodeAssociationId, NodeId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/detach_node_from_group/__init__.py b/src/conode/application/detach_node_from_group/__init__.py similarity index 100% rename from src/prodik/application/detach_node_from_group/__init__.py rename to src/conode/application/detach_node_from_group/__init__.py diff --git a/src/prodik/application/detach_node_from_group/detach_node.py b/src/conode/application/detach_node_from_group/detach_node.py similarity index 82% rename from src/prodik/application/detach_node_from_group/detach_node.py rename to src/conode/application/detach_node_from_group/detach_node.py index 6a9707f..36c2eac 100644 --- a/src/prodik/application/detach_node_from_group/detach_node.py +++ b/src/conode/application/detach_node_from_group/detach_node.py @@ -1,17 +1,17 @@ from dataclasses import dataclass -from prodik.application.errors import ( +from conode.application.errors import ( GroupNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.node import NodeAssociationId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.node import NodeAssociationId @dataclass diff --git a/src/prodik/application/errors.py b/src/conode/application/errors.py similarity index 100% rename from src/prodik/application/errors.py rename to src/conode/application/errors.py diff --git a/src/prodik/application/interfaces/__init__.py b/src/conode/application/interfaces/__init__.py similarity index 100% rename from src/prodik/application/interfaces/__init__.py rename to src/conode/application/interfaces/__init__.py diff --git a/src/prodik/application/interfaces/identity_provider.py b/src/conode/application/interfaces/identity_provider.py similarity index 70% rename from src/prodik/application/interfaces/identity_provider.py rename to src/conode/application/interfaces/identity_provider.py index 4c96635..a3e986a 100644 --- a/src/prodik/application/interfaces/identity_provider.py +++ b/src/conode/application/interfaces/identity_provider.py @@ -1,6 +1,6 @@ from typing import Protocol -from prodik.application.interfaces.token_managers import UserMeta +from conode.application.interfaces.token_manager import UserMeta class IdentityProvider(Protocol): diff --git a/src/prodik/application/interfaces/password_hasher.py b/src/conode/application/interfaces/password_hasher.py similarity index 100% rename from src/prodik/application/interfaces/password_hasher.py rename to src/conode/application/interfaces/password_hasher.py diff --git a/src/prodik/application/interfaces/repositories/__init__.py b/src/conode/application/interfaces/repositories/__init__.py similarity index 87% rename from src/prodik/application/interfaces/repositories/__init__.py rename to src/conode/application/interfaces/repositories/__init__.py index 8fd5625..c1879b0 100644 --- a/src/prodik/application/interfaces/repositories/__init__.py +++ b/src/conode/application/interfaces/repositories/__init__.py @@ -1,4 +1,3 @@ -from .authorization import LocalAuthorizationRepository, SessionRepository from .company import CompanyRepository from .context import ContextRepository from .contract import ContractRepository @@ -23,7 +22,6 @@ "ContractRepository", "EdgeRepository", "GroupRepository", - "LocalAuthorizationRepository", "NodeAssociationRepository", "NodeRepository", "OfferContextRepository", @@ -32,7 +30,6 @@ "OfferRepository", "RolePermissionsRepository", "RoleRepository", - "SessionRepository", "UserGrantRepository", "UserRepository", ) diff --git a/src/prodik/application/interfaces/repositories/company.py b/src/conode/application/interfaces/repositories/company.py similarity index 82% rename from src/prodik/application/interfaces/repositories/company.py rename to src/conode/application/interfaces/repositories/company.py index 5087e6a..ed7b359 100644 --- a/src/prodik/application/interfaces/repositories/company.py +++ b/src/conode/application/interfaces/repositories/company.py @@ -1,7 +1,7 @@ from typing import Protocol -from prodik.domain.company import Company, CompanyId, CompanyName -from prodik.domain.user import UserId +from conode.domain.company import Company, CompanyId, CompanyName +from conode.domain.user import UserId class CompanyRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/context.py b/src/conode/application/interfaces/repositories/context.py similarity index 81% rename from src/prodik/application/interfaces/repositories/context.py rename to src/conode/application/interfaces/repositories/context.py index cf9e547..67efd27 100644 --- a/src/prodik/application/interfaces/repositories/context.py +++ b/src/conode/application/interfaces/repositories/context.py @@ -1,7 +1,7 @@ from typing import Protocol -from prodik.domain.company import CompanyId -from prodik.domain.context import Context, ContextId +from conode.domain.company import CompanyId +from conode.domain.context import Context, ContextId class ContextRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/contract.py b/src/conode/application/interfaces/repositories/contract.py similarity index 74% rename from src/prodik/application/interfaces/repositories/contract.py rename to src/conode/application/interfaces/repositories/contract.py index 02cd78d..a4d1118 100644 --- a/src/prodik/application/interfaces/repositories/contract.py +++ b/src/conode/application/interfaces/repositories/contract.py @@ -1,6 +1,6 @@ from typing import Protocol -from prodik.domain.contract import Contract +from conode.domain.contract import Contract class ContractRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/edge.py b/src/conode/application/interfaces/repositories/edge.py similarity index 82% rename from src/prodik/application/interfaces/repositories/edge.py rename to src/conode/application/interfaces/repositories/edge.py index dab769e..fc97385 100644 --- a/src/prodik/application/interfaces/repositories/edge.py +++ b/src/conode/application/interfaces/repositories/edge.py @@ -1,8 +1,8 @@ from typing import Protocol -from prodik.domain.context import ContextId -from prodik.domain.edge import Edge, EdgeId -from prodik.domain.node import NodeId +from conode.domain.context import ContextId +from conode.domain.edge import Edge, EdgeId +from conode.domain.node import NodeId class EdgeRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/grant/__init__.py b/src/conode/application/interfaces/repositories/grant/__init__.py similarity index 100% rename from src/prodik/application/interfaces/repositories/grant/__init__.py rename to src/conode/application/interfaces/repositories/grant/__init__.py diff --git a/src/prodik/application/interfaces/repositories/grant/company.py b/src/conode/application/interfaces/repositories/grant/company.py similarity index 100% rename from src/prodik/application/interfaces/repositories/grant/company.py rename to src/conode/application/interfaces/repositories/grant/company.py diff --git a/src/prodik/application/interfaces/repositories/grant/user.py b/src/conode/application/interfaces/repositories/grant/user.py similarity index 77% rename from src/prodik/application/interfaces/repositories/grant/user.py rename to src/conode/application/interfaces/repositories/grant/user.py index 66f3896..341a6cd 100644 --- a/src/prodik/application/interfaces/repositories/grant/user.py +++ b/src/conode/application/interfaces/repositories/grant/user.py @@ -1,8 +1,8 @@ from typing import Protocol -from prodik.domain.grant import UserGrant -from prodik.domain.role import RoleId -from prodik.domain.user import UserId +from conode.domain.grant import UserGrant +from conode.domain.role import RoleId +from conode.domain.user import UserId class UserGrantRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/group.py b/src/conode/application/interfaces/repositories/group.py similarity index 81% rename from src/prodik/application/interfaces/repositories/group.py rename to src/conode/application/interfaces/repositories/group.py index 8570233..3f08740 100644 --- a/src/prodik/application/interfaces/repositories/group.py +++ b/src/conode/application/interfaces/repositories/group.py @@ -1,7 +1,7 @@ from typing import Protocol -from prodik.domain.company import CompanyId -from prodik.domain.group import Group, GroupId +from conode.domain.company import CompanyId +from conode.domain.group import Group, GroupId class GroupRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/node.py b/src/conode/application/interfaces/repositories/node.py similarity index 88% rename from src/prodik/application/interfaces/repositories/node.py rename to src/conode/application/interfaces/repositories/node.py index 38acf87..15d94a5 100644 --- a/src/prodik/application/interfaces/repositories/node.py +++ b/src/conode/application/interfaces/repositories/node.py @@ -1,8 +1,8 @@ from typing import Protocol -from prodik.domain.company import CompanyId -from prodik.domain.group import GroupId -from prodik.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId +from conode.domain.company import CompanyId +from conode.domain.group import GroupId +from conode.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId class NodeRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/offer.py b/src/conode/application/interfaces/repositories/offer.py similarity index 93% rename from src/prodik/application/interfaces/repositories/offer.py rename to src/conode/application/interfaces/repositories/offer.py index 75bde1b..9bfdbf0 100644 --- a/src/prodik/application/interfaces/repositories/offer.py +++ b/src/conode/application/interfaces/repositories/offer.py @@ -1,6 +1,6 @@ from typing import Protocol -from prodik.domain.offer import Offer, OfferContext, OfferGroup, OfferId, OfferLink +from conode.domain.offer import Offer, OfferContext, OfferGroup, OfferId, OfferLink class OfferRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/permissions.py b/src/conode/application/interfaces/repositories/permissions.py similarity index 89% rename from src/prodik/application/interfaces/repositories/permissions.py rename to src/conode/application/interfaces/repositories/permissions.py index 4229932..c87efb6 100644 --- a/src/prodik/application/interfaces/repositories/permissions.py +++ b/src/conode/application/interfaces/repositories/permissions.py @@ -1,6 +1,6 @@ from typing import Protocol -from prodik.domain.role import RoleId, RolePermission, RolePermissionId +from conode.domain.role import RoleId, RolePermission, RolePermissionId class RolePermissionsRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/role.py b/src/conode/application/interfaces/repositories/role.py similarity index 82% rename from src/prodik/application/interfaces/repositories/role.py rename to src/conode/application/interfaces/repositories/role.py index 7e520a3..ffe2111 100644 --- a/src/prodik/application/interfaces/repositories/role.py +++ b/src/conode/application/interfaces/repositories/role.py @@ -1,7 +1,7 @@ from typing import Protocol -from prodik.domain.company import CompanyId -from prodik.domain.role import Role, RoleId, RoleName +from conode.domain.company import CompanyId +from conode.domain.role import Role, RoleId, RoleName class RoleRepository(Protocol): diff --git a/src/prodik/application/interfaces/repositories/user.py b/src/conode/application/interfaces/repositories/user.py similarity index 89% rename from src/prodik/application/interfaces/repositories/user.py rename to src/conode/application/interfaces/repositories/user.py index f1152f8..05c29f5 100644 --- a/src/prodik/application/interfaces/repositories/user.py +++ b/src/conode/application/interfaces/repositories/user.py @@ -1,6 +1,6 @@ from typing import Protocol -from prodik.domain.user import Email, User, UserId, Username +from conode.domain.user import Email, User, UserId, Username class UserRepository(Protocol): diff --git a/src/conode/application/interfaces/token_manager/__init__.py b/src/conode/application/interfaces/token_manager/__init__.py new file mode 100644 index 0000000..49ee8bb --- /dev/null +++ b/src/conode/application/interfaces/token_manager/__init__.py @@ -0,0 +1,11 @@ +from .token_manager import ( + TokenManager, + TokenManagerResponse, + UserMeta, +) + +__all__ = ( + "TokenManager", + "TokenManagerResponse", + "UserMeta", +) diff --git a/src/conode/application/interfaces/token_manager/token_manager.py b/src/conode/application/interfaces/token_manager/token_manager.py new file mode 100644 index 0000000..5d67513 --- /dev/null +++ b/src/conode/application/interfaces/token_manager/token_manager.py @@ -0,0 +1,21 @@ +from typing import NamedTuple, Protocol, TypedDict + +from conode.domain.user import User, UserSystemRole + + +class UserMeta(TypedDict): + system_role: UserSystemRole + first_name: str + last_name: str + email: str + username: str + + +class TokenManagerResponse(NamedTuple): + token: str + expires_in: int + + +class TokenManager(Protocol): + def decode(self, token: str) -> UserMeta: ... + def encode(self, user: User) -> str: ... diff --git a/src/prodik/application/interfaces/transaction_manager.py b/src/conode/application/interfaces/transaction_manager.py similarity index 100% rename from src/prodik/application/interfaces/transaction_manager.py rename to src/conode/application/interfaces/transaction_manager.py diff --git a/src/prodik/application/manage_context/__init__.py b/src/conode/application/manage_context/__init__.py similarity index 100% rename from src/prodik/application/manage_context/__init__.py rename to src/conode/application/manage_context/__init__.py diff --git a/src/prodik/application/manage_context/create_context.py b/src/conode/application/manage_context/create_context.py similarity index 80% rename from src/prodik/application/manage_context/create_context.py rename to src/conode/application/manage_context/create_context.py index 8db86b2..8e5fc77 100644 --- a/src/prodik/application/manage_context/create_context.py +++ b/src/conode/application/manage_context/create_context.py @@ -1,14 +1,14 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.company import CompanyId -from prodik.domain.context import Context, ContextId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.company import CompanyId +from conode.domain.context import Context, ContextId @dataclass diff --git a/src/prodik/application/manage_context/delete_context.py b/src/conode/application/manage_context/delete_context.py similarity index 77% rename from src/prodik/application/manage_context/delete_context.py rename to src/conode/application/manage_context/delete_context.py index e150bf2..b98d86a 100644 --- a/src/prodik/application/manage_context/delete_context.py +++ b/src/conode/application/manage_context/delete_context.py @@ -1,12 +1,12 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.context import ContextId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.context import ContextId @dataclass diff --git a/src/prodik/application/manage_edge/__init__.py b/src/conode/application/manage_edge/__init__.py similarity index 100% rename from src/prodik/application/manage_edge/__init__.py rename to src/conode/application/manage_edge/__init__.py diff --git a/src/prodik/application/manage_edge/create_edge.py b/src/conode/application/manage_edge/create_edge.py similarity index 85% rename from src/prodik/application/manage_edge/create_edge.py rename to src/conode/application/manage_edge/create_edge.py index 02fac01..45e0ded 100644 --- a/src/prodik/application/manage_edge/create_edge.py +++ b/src/conode/application/manage_edge/create_edge.py @@ -2,20 +2,20 @@ from typing import Final from uuid import uuid4 -from prodik.application.errors import ( +from conode.application.errors import ( NodeNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.context import ContextId -from prodik.domain.edge import Edge, EdgeId -from prodik.domain.node import NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.context import ContextId +from conode.domain.edge import Edge, EdgeId +from conode.domain.node import NodeId EXPECTED_COUNT_OF_NODES: Final = 2 diff --git a/src/prodik/application/manage_edge/delete_edge.py b/src/conode/application/manage_edge/delete_edge.py similarity index 80% rename from src/prodik/application/manage_edge/delete_edge.py rename to src/conode/application/manage_edge/delete_edge.py index 9b11e2b..0ca6a4b 100644 --- a/src/prodik/application/manage_edge/delete_edge.py +++ b/src/conode/application/manage_edge/delete_edge.py @@ -1,13 +1,13 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.edge import EdgeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.edge import EdgeId @dataclass diff --git a/src/prodik/application/manage_group/__init__.py b/src/conode/application/manage_group/__init__.py similarity index 100% rename from src/prodik/application/manage_group/__init__.py rename to src/conode/application/manage_group/__init__.py diff --git a/src/prodik/application/manage_group/create_group.py b/src/conode/application/manage_group/create_group.py similarity index 84% rename from src/prodik/application/manage_group/create_group.py rename to src/conode/application/manage_group/create_group.py index 89783d6..53414e5 100644 --- a/src/prodik/application/manage_group/create_group.py +++ b/src/conode/application/manage_group/create_group.py @@ -1,14 +1,14 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.company import CompanyId -from prodik.domain.group import Group, GroupId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.company import CompanyId +from conode.domain.group import Group, GroupId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/manage_group/delete_group.py b/src/conode/application/manage_group/delete_group.py similarity index 77% rename from src/prodik/application/manage_group/delete_group.py rename to src/conode/application/manage_group/delete_group.py index 7c87ece..cd04f3a 100644 --- a/src/prodik/application/manage_group/delete_group.py +++ b/src/conode/application/manage_group/delete_group.py @@ -1,12 +1,12 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.group import GroupId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.group import GroupId @dataclass diff --git a/src/prodik/application/manage_node/__init__.py b/src/conode/application/manage_node/__init__.py similarity index 100% rename from src/prodik/application/manage_node/__init__.py rename to src/conode/application/manage_node/__init__.py diff --git a/src/prodik/application/manage_node/create_node.py b/src/conode/application/manage_node/create_node.py similarity index 85% rename from src/prodik/application/manage_node/create_node.py rename to src/conode/application/manage_node/create_node.py index a3b287d..49e31b6 100644 --- a/src/prodik/application/manage_node/create_node.py +++ b/src/conode/application/manage_node/create_node.py @@ -1,16 +1,16 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.group import GroupId -from prodik.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.group import GroupId +from conode.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/manage_node/delete_node.py b/src/conode/application/manage_node/delete_node.py similarity index 84% rename from src/prodik/application/manage_node/delete_node.py rename to src/conode/application/manage_node/delete_node.py index 9f65c9b..6fe8753 100644 --- a/src/prodik/application/manage_node/delete_node.py +++ b/src/conode/application/manage_node/delete_node.py @@ -1,14 +1,14 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.node import NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.node import NodeId @dataclass diff --git a/src/prodik/application/manage_profile/__init__.py b/src/conode/application/manage_profile/__init__.py similarity index 100% rename from src/prodik/application/manage_profile/__init__.py rename to src/conode/application/manage_profile/__init__.py diff --git a/src/prodik/application/manage_profile/update_current_user_profile.py b/src/conode/application/manage_profile/update_current_user_profile.py similarity index 53% rename from src/prodik/application/manage_profile/update_current_user_profile.py rename to src/conode/application/manage_profile/update_current_user_profile.py index 916b65a..9bec12b 100644 --- a/src/prodik/application/manage_profile/update_current_user_profile.py +++ b/src/conode/application/manage_profile/update_current_user_profile.py @@ -1,10 +1,8 @@ from dataclasses import dataclass -from prodik.application.errors import UserAlreadyExistsError -from prodik.application.interfaces.repositories import UserRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.user import Email +from conode.application.interfaces.repositories import UserRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService @dataclass @@ -12,7 +10,6 @@ class UpdateCurrentUserProfileRequestDTO: username: str first_name: str last_name: str - email: str bio: str @@ -26,17 +23,9 @@ async def execute(self, request: UpdateCurrentUserProfileRequestDTO) -> None: async with self.transaction_manager: user = await self.access_control_service.get_authorized_user() - unique_user = await self.user_repository.get_by_email(Email(request.email)) - if unique_user is not None and unique_user != user: - raise UserAlreadyExistsError( - "User with this email already exists", - [{"key": "email", "value": request.email}], - ) - user.update_profile( first_name=request.first_name, last_name=request.last_name, username=request.username, - email=request.email, bio=request.bio, ) diff --git a/src/prodik/application/manage_role/__init__.py b/src/conode/application/manage_role/__init__.py similarity index 100% rename from src/prodik/application/manage_role/__init__.py rename to src/conode/application/manage_role/__init__.py diff --git a/src/prodik/application/manage_role/create_role.py b/src/conode/application/manage_role/create_role.py similarity index 88% rename from src/prodik/application/manage_role/create_role.py rename to src/conode/application/manage_role/create_role.py index d6cb9f4..b0bd9fa 100644 --- a/src/prodik/application/manage_role/create_role.py +++ b/src/conode/application/manage_role/create_role.py @@ -1,15 +1,15 @@ from dataclasses import dataclass -from prodik.application.errors import RoleAlreadyExistsError -from prodik.application.interfaces.repositories import ( +from conode.application.errors import RoleAlreadyExistsError +from conode.application.interfaces.repositories import ( CompanyRepository, RolePermissionsRepository, RoleRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService, RoleManagmentService -from prodik.domain.company import CompanyId -from prodik.domain.role import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService, RoleManagmentService +from conode.domain.company import CompanyId +from conode.domain.role import ( EntityType, PermissionType, Role, diff --git a/src/prodik/application/manage_role/delete_role.py b/src/conode/application/manage_role/delete_role.py similarity index 74% rename from src/prodik/application/manage_role/delete_role.py rename to src/conode/application/manage_role/delete_role.py index b6b1e09..5a83e37 100644 --- a/src/prodik/application/manage_role/delete_role.py +++ b/src/conode/application/manage_role/delete_role.py @@ -1,9 +1,9 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import RoleRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.role import RoleId +from conode.application.interfaces.repositories import RoleRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.role import RoleId @dataclass diff --git a/src/prodik/application/manage_role/update_role.py b/src/conode/application/manage_role/update_role.py similarity index 91% rename from src/prodik/application/manage_role/update_role.py rename to src/conode/application/manage_role/update_role.py index f2d4c53..fe2460c 100644 --- a/src/prodik/application/manage_role/update_role.py +++ b/src/conode/application/manage_role/update_role.py @@ -1,12 +1,12 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( RolePermissionsRepository, RoleRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.role import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.role import ( EntityType, PermissionType, Role, diff --git a/src/prodik/application/manage_user_rights/__init__.py b/src/conode/application/manage_user_rights/__init__.py similarity index 100% rename from src/prodik/application/manage_user_rights/__init__.py rename to src/conode/application/manage_user_rights/__init__.py diff --git a/src/prodik/application/manage_user_rights/give_role_to_user.py b/src/conode/application/manage_user_rights/give_role_to_user.py similarity index 81% rename from src/prodik/application/manage_user_rights/give_role_to_user.py rename to src/conode/application/manage_user_rights/give_role_to_user.py index db70484..1ad1d18 100644 --- a/src/prodik/application/manage_user_rights/give_role_to_user.py +++ b/src/conode/application/manage_user_rights/give_role_to_user.py @@ -1,19 +1,19 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.errors import ( +from conode.application.errors import ( GrantAlreadyExistsError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( RoleRepository, UserGrantRepository, UserRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.grant import UserGrant, UserGrantId -from prodik.domain.role import RoleId -from prodik.domain.user import UserId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.grant import UserGrant, UserGrantId +from conode.domain.role import RoleId +from conode.domain.user import UserId @dataclass diff --git a/src/prodik/application/manage_user_rights/revoke_role_from_user.py b/src/conode/application/manage_user_rights/revoke_role_from_user.py similarity index 83% rename from src/prodik/application/manage_user_rights/revoke_role_from_user.py rename to src/conode/application/manage_user_rights/revoke_role_from_user.py index 09a4ff9..455911a 100644 --- a/src/prodik/application/manage_user_rights/revoke_role_from_user.py +++ b/src/conode/application/manage_user_rights/revoke_role_from_user.py @@ -1,17 +1,17 @@ from dataclasses import dataclass -from prodik.application.errors import ( +from conode.application.errors import ( GrantNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( RoleRepository, UserGrantRepository, UserRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.role import RoleId -from prodik.domain.user import UserId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.role import RoleId +from conode.domain.user import UserId @dataclass diff --git a/src/prodik/application/receive_context_info/__init__.py b/src/conode/application/receive_context_info/__init__.py similarity index 100% rename from src/prodik/application/receive_context_info/__init__.py rename to src/conode/application/receive_context_info/__init__.py diff --git a/src/prodik/application/receive_context_info/get_context_by_id.py b/src/conode/application/receive_context_info/get_context_by_id.py similarity index 74% rename from src/prodik/application/receive_context_info/get_context_by_id.py rename to src/conode/application/receive_context_info/get_context_by_id.py index 681cc83..0668b38 100644 --- a/src/prodik/application/receive_context_info/get_context_by_id.py +++ b/src/conode/application/receive_context_info/get_context_by_id.py @@ -1,8 +1,8 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ContextRepository -from prodik.application.services import AccessControlService -from prodik.domain.context import Context, ContextId +from conode.application.interfaces.repositories import ContextRepository +from conode.application.services import AccessControlService +from conode.domain.context import Context, ContextId @dataclass diff --git a/src/prodik/application/receive_graph_statistics/__init__.py b/src/conode/application/receive_graph_statistics/__init__.py similarity index 100% rename from src/prodik/application/receive_graph_statistics/__init__.py rename to src/conode/application/receive_graph_statistics/__init__.py diff --git a/src/prodik/application/receive_graph_statistics/neighbours.py b/src/conode/application/receive_graph_statistics/neighbours.py similarity index 86% rename from src/prodik/application/receive_graph_statistics/neighbours.py rename to src/conode/application/receive_graph_statistics/neighbours.py index e638b39..bf378ff 100644 --- a/src/prodik/application/receive_graph_statistics/neighbours.py +++ b/src/conode/application/receive_graph_statistics/neighbours.py @@ -1,16 +1,16 @@ from collections import deque from dataclasses import dataclass -from prodik.application.errors import DepthCannotBeNegativeError -from prodik.application.interfaces.repositories import ( +from conode.application.errors import DepthCannotBeNegativeError +from conode.application.interfaces.repositories import ( ContextRepository, EdgeRepository, NodeRepository, ) -from prodik.application.services import AccessControlService -from prodik.domain.context import ContextId -from prodik.domain.edge import Edge -from prodik.domain.node import Node, NodeId +from conode.application.services import AccessControlService +from conode.domain.context import ContextId +from conode.domain.edge import Edge +from conode.domain.node import Node, NodeId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/receive_graph_statistics/shortest_path.py b/src/conode/application/receive_graph_statistics/shortest_path.py similarity index 91% rename from src/prodik/application/receive_graph_statistics/shortest_path.py rename to src/conode/application/receive_graph_statistics/shortest_path.py index 59018b6..68ae5cb 100644 --- a/src/prodik/application/receive_graph_statistics/shortest_path.py +++ b/src/conode/application/receive_graph_statistics/shortest_path.py @@ -3,11 +3,11 @@ from heapq import heappop, heappush from math import inf -from prodik.application.interfaces.repositories import ContextRepository, EdgeRepository -from prodik.application.services import AccessControlService -from prodik.domain.context import ContextId -from prodik.domain.edge import Edge -from prodik.domain.node import NodeId +from conode.application.interfaces.repositories import ContextRepository, EdgeRepository +from conode.application.services import AccessControlService +from conode.domain.context import ContextId +from conode.domain.edge import Edge +from conode.domain.node import NodeId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/receive_group_info/__init__.py b/src/conode/application/receive_group_info/__init__.py similarity index 100% rename from src/prodik/application/receive_group_info/__init__.py rename to src/conode/application/receive_group_info/__init__.py diff --git a/src/prodik/application/receive_group_info/get_group_by_id.py b/src/conode/application/receive_group_info/get_group_by_id.py similarity index 74% rename from src/prodik/application/receive_group_info/get_group_by_id.py rename to src/conode/application/receive_group_info/get_group_by_id.py index 6d8f722..8529b43 100644 --- a/src/prodik/application/receive_group_info/get_group_by_id.py +++ b/src/conode/application/receive_group_info/get_group_by_id.py @@ -1,8 +1,8 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import GroupRepository -from prodik.application.services import AccessControlService -from prodik.domain.group import Group, GroupId +from conode.application.interfaces.repositories import GroupRepository +from conode.application.services import AccessControlService +from conode.domain.group import Group, GroupId @dataclass diff --git a/src/prodik/application/receive_node_info/__init__.py b/src/conode/application/receive_node_info/__init__.py similarity index 100% rename from src/prodik/application/receive_node_info/__init__.py rename to src/conode/application/receive_node_info/__init__.py diff --git a/src/prodik/application/receive_node_info/get_nodes_by_group.py b/src/conode/application/receive_node_info/get_nodes_by_group.py similarity index 82% rename from src/prodik/application/receive_node_info/get_nodes_by_group.py rename to src/conode/application/receive_node_info/get_nodes_by_group.py index 559e599..f500eb3 100644 --- a/src/prodik/application/receive_node_info/get_nodes_by_group.py +++ b/src/conode/application/receive_node_info/get_nodes_by_group.py @@ -1,14 +1,14 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.services import AccessControlService -from prodik.domain.group import GroupId -from prodik.domain.node import Node +from conode.application.services import AccessControlService +from conode.domain.group import GroupId +from conode.domain.node import Node @dataclass diff --git a/src/prodik/application/receive_user_info/__init__.py b/src/conode/application/receive_user_info/__init__.py similarity index 100% rename from src/prodik/application/receive_user_info/__init__.py rename to src/conode/application/receive_user_info/__init__.py diff --git a/src/prodik/application/receive_user_info/get_current_user.py b/src/conode/application/receive_user_info/get_current_user.py similarity index 71% rename from src/prodik/application/receive_user_info/get_current_user.py rename to src/conode/application/receive_user_info/get_current_user.py index eae5e6b..80f32e8 100644 --- a/src/prodik/application/receive_user_info/get_current_user.py +++ b/src/conode/application/receive_user_info/get_current_user.py @@ -1,7 +1,7 @@ from dataclasses import dataclass -from prodik.application.services import AccessControlService -from prodik.domain.user import User +from conode.application.services import AccessControlService +from conode.domain.user import User @dataclass diff --git a/src/prodik/application/receive_user_info/get_user_by_username.py b/src/conode/application/receive_user_info/get_user_by_username.py similarity index 71% rename from src/prodik/application/receive_user_info/get_user_by_username.py rename to src/conode/application/receive_user_info/get_user_by_username.py index f67eee5..48a867d 100644 --- a/src/prodik/application/receive_user_info/get_user_by_username.py +++ b/src/conode/application/receive_user_info/get_user_by_username.py @@ -1,9 +1,9 @@ from dataclasses import dataclass -from prodik.application.errors import UserNotFoundError -from prodik.application.interfaces.repositories import UserRepository -from prodik.application.services import AccessControlService -from prodik.domain.user import User, Username +from conode.application.errors import UserNotFoundError +from conode.application.interfaces.repositories import UserRepository +from conode.application.services import AccessControlService +from conode.domain.user import User, Username @dataclass diff --git a/src/prodik/application/register_company/__init__.py b/src/conode/application/register_company/__init__.py similarity index 100% rename from src/prodik/application/register_company/__init__.py rename to src/conode/application/register_company/__init__.py diff --git a/src/prodik/application/register_company/register_company.py b/src/conode/application/register_company/register_company.py similarity index 87% rename from src/prodik/application/register_company/register_company.py rename to src/conode/application/register_company/register_company.py index 4194e69..783d32b 100644 --- a/src/prodik/application/register_company/register_company.py +++ b/src/conode/application/register_company/register_company.py @@ -1,18 +1,18 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.errors import CompanyAlreadyExistsError -from prodik.application.interfaces.repositories import ( +from conode.application.errors import CompanyAlreadyExistsError +from conode.application.interfaces.repositories import ( CompanyRepository, RolePermissionsRepository, RoleRepository, UserGrantRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService, RoleManagmentService -from prodik.domain.company import Company, CompanyId, CompanyName -from prodik.domain.grant import UserGrant, UserGrantId -from prodik.domain.role import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService, RoleManagmentService +from conode.domain.company import Company, CompanyId, CompanyName +from conode.domain.grant import UserGrant, UserGrantId +from conode.domain.role import ( EntityType, PermissionType, ) diff --git a/src/prodik/application/services/__init__.py b/src/conode/application/services/__init__.py similarity index 100% rename from src/prodik/application/services/__init__.py rename to src/conode/application/services/__init__.py diff --git a/src/prodik/application/services/access_control.py b/src/conode/application/services/access_control.py similarity index 88% rename from src/prodik/application/services/access_control.py rename to src/conode/application/services/access_control.py index 37febd5..d2d31b4 100644 --- a/src/prodik/application/services/access_control.py +++ b/src/conode/application/services/access_control.py @@ -1,30 +1,30 @@ from dataclasses import dataclass from typing import Final +from uuid import uuid4 -from prodik.application.errors import ( +from conode.application.errors import ( NotEnoughRightsError, - SessionExpiredError, ) -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.identity_provider import IdentityProvider +from conode.application.interfaces.repositories import ( CompanyRepository, RolePermissionsRepository, RoleRepository, UserGrantRepository, UserRepository, ) -from prodik.application.interfaces.token_managers import UserMeta -from prodik.domain.company import Company -from prodik.domain.context import Context -from prodik.domain.group import Group -from prodik.domain.role import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.company import Company +from conode.domain.context import Context +from conode.domain.group import Group +from conode.domain.role import ( EntityType, PermissionType, Role, RolePermission, RolePermissionEntityId, ) -from prodik.domain.user import User +from conode.domain.user import Email, User, UserId type RolesPermissions = list[RolePermission] @@ -41,12 +41,9 @@ class AccessControlService: role_repository: RoleRepository company_repository: CompanyRepository identity_provider: IdentityProvider + transaction_manager: TransactionManager user_repository: UserRepository - def _ensure_revision_is_valid(self, meta: UserMeta, user: User) -> None: - if meta["revision"] != user.token_revision: - raise SessionExpiredError("Session has expired", None) - async def _get_all_permissions(self, user: User) -> list[RolePermission]: grants = await self.user_grant_repository.get_all_by_user_id(user.id) role_ids = [g.role_id for g in grants] @@ -56,9 +53,18 @@ async def _get_all_permissions(self, user: User) -> list[RolePermission]: async def get_authorized_user(self) -> User: meta = self.identity_provider.get_current_user_meta() - user = await self.user_repository.get_by_id(meta["user_id"]) - - self._ensure_revision_is_valid(meta, user) + user = await self.user_repository.get_by_email(Email(meta["email"])) + if user is None: + async with self.transaction_manager: + user = User.new( + user_id=UserId(uuid4()), + first_name=meta["first_name"], + last_name=meta["last_name"], + username=meta["username"], + email=meta["email"], + bio="Beautiful description about me!", + ) + await self.user_repository.create(user) return user diff --git a/src/prodik/application/services/offer_acceptance.py b/src/conode/application/services/offer_acceptance.py similarity index 89% rename from src/prodik/application/services/offer_acceptance.py rename to src/conode/application/services/offer_acceptance.py index 8c1386a..f4fb4ba 100644 --- a/src/prodik/application/services/offer_acceptance.py +++ b/src/conode/application/services/offer_acceptance.py @@ -1,10 +1,10 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.domain.company import Company -from prodik.domain.contract import Contract, ContractId -from prodik.domain.offer import Offer, OfferLink -from prodik.domain.role import RoleId +from conode.domain.company import Company +from conode.domain.contract import Contract, ContractId +from conode.domain.offer import Offer, OfferLink +from conode.domain.role import RoleId @dataclass(slots=True, frozen=True, kw_only=True) diff --git a/src/prodik/application/services/offer_sending.py b/src/conode/application/services/offer_sending.py similarity index 91% rename from src/prodik/application/services/offer_sending.py rename to src/conode/application/services/offer_sending.py index 5e5bfbf..660f37a 100644 --- a/src/prodik/application/services/offer_sending.py +++ b/src/conode/application/services/offer_sending.py @@ -2,10 +2,10 @@ from datetime import datetime from uuid import uuid4 -from prodik.domain.company import Company -from prodik.domain.context import Context, ContextId -from prodik.domain.group import Group, GroupId -from prodik.domain.offer import ( +from conode.domain.company import Company +from conode.domain.context import Context, ContextId +from conode.domain.group import Group, GroupId +from conode.domain.offer import ( Offer, OfferContext, OfferContextId, @@ -15,7 +15,7 @@ OfferLink, OfferLinkId, ) -from prodik.domain.role import PermissionType +from conode.domain.role import PermissionType @dataclass(slots=True, frozen=True, kw_only=True) diff --git a/src/prodik/application/services/role_managment.py b/src/conode/application/services/role_managment.py similarity index 94% rename from src/prodik/application/services/role_managment.py rename to src/conode/application/services/role_managment.py index 6755a9e..0ee694c 100644 --- a/src/prodik/application/services/role_managment.py +++ b/src/conode/application/services/role_managment.py @@ -1,8 +1,8 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.domain.company import Company -from prodik.domain.role import ( +from conode.domain.company import Company +from conode.domain.role import ( EntityType, PermissionType, Role, diff --git a/src/prodik/application/share_graph/__init__.py b/src/conode/application/share_graph/__init__.py similarity index 100% rename from src/prodik/application/share_graph/__init__.py rename to src/conode/application/share_graph/__init__.py diff --git a/src/prodik/application/share_graph/accept_offer.py b/src/conode/application/share_graph/accept_offer.py similarity index 94% rename from src/prodik/application/share_graph/accept_offer.py rename to src/conode/application/share_graph/accept_offer.py index 4252179..c1642fe 100644 --- a/src/prodik/application/share_graph/accept_offer.py +++ b/src/conode/application/share_graph/accept_offer.py @@ -2,11 +2,11 @@ from dataclasses import dataclass from typing import cast -from prodik.application.errors import ( +from conode.application.errors import ( CompanyNotFoundError, OfferLinkNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContractRepository, OfferContextRepository, @@ -16,16 +16,16 @@ RolePermissionsRepository, RoleRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import ( AccessControlService, OfferAcceptanceService, RoleManagmentService, ) -from prodik.domain.company import Company -from prodik.domain.contract import Contract -from prodik.domain.offer import Offer, OfferContext, OfferGroup, OfferId -from prodik.domain.role import EntityType, PermissionType, RolePermissionEntityId +from conode.domain.company import Company +from conode.domain.contract import Contract +from conode.domain.offer import Offer, OfferContext, OfferGroup, OfferId +from conode.domain.role import EntityType, PermissionType, RolePermissionEntityId @dataclass diff --git a/src/prodik/application/share_graph/decline_offer.py b/src/conode/application/share_graph/decline_offer.py similarity index 87% rename from src/prodik/application/share_graph/decline_offer.py rename to src/conode/application/share_graph/decline_offer.py index a52428b..a57ae2f 100644 --- a/src/prodik/application/share_graph/decline_offer.py +++ b/src/conode/application/share_graph/decline_offer.py @@ -1,16 +1,16 @@ from dataclasses import dataclass -from prodik.application.errors import ( +from conode.application.errors import ( OfferLinkNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, OfferLinkRepository, OfferRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.offer import OfferId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.offer import OfferId @dataclass diff --git a/src/prodik/application/share_graph/send_offer_to_company.py b/src/conode/application/share_graph/send_offer_to_company.py similarity index 90% rename from src/prodik/application/share_graph/send_offer_to_company.py rename to src/conode/application/share_graph/send_offer_to_company.py index e90762d..fd67456 100644 --- a/src/prodik/application/share_graph/send_offer_to_company.py +++ b/src/conode/application/share_graph/send_offer_to_company.py @@ -1,12 +1,12 @@ from dataclasses import dataclass from datetime import datetime -from prodik.application.errors import ( +from conode.application.errors import ( ContextNotFoundError, GroupNotFoundError, OfferNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, GroupRepository, @@ -15,13 +15,13 @@ OfferLinkRepository, OfferRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService, OfferSendingService -from prodik.domain.company import CompanyId -from prodik.domain.context import ContextId -from prodik.domain.group import GroupId -from prodik.domain.offer import Offer, OfferId -from prodik.domain.role import PermissionType +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService, OfferSendingService +from conode.domain.company import CompanyId +from conode.domain.context import ContextId +from conode.domain.group import GroupId +from conode.domain.offer import Offer, OfferId +from conode.domain.role import PermissionType @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/update_edge_weight/__init__.py b/src/conode/application/update_edge_weight/__init__.py similarity index 100% rename from src/prodik/application/update_edge_weight/__init__.py rename to src/conode/application/update_edge_weight/__init__.py diff --git a/src/prodik/application/update_edge_weight/decrement_edge_weight.py b/src/conode/application/update_edge_weight/decrement_edge_weight.py similarity index 80% rename from src/prodik/application/update_edge_weight/decrement_edge_weight.py rename to src/conode/application/update_edge_weight/decrement_edge_weight.py index 781e30c..eda3fd3 100644 --- a/src/prodik/application/update_edge_weight/decrement_edge_weight.py +++ b/src/conode/application/update_edge_weight/decrement_edge_weight.py @@ -1,13 +1,13 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.edge import EdgeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.edge import EdgeId @dataclass diff --git a/src/prodik/application/update_edge_weight/increment_edge_weight.py b/src/conode/application/update_edge_weight/increment_edge_weight.py similarity index 80% rename from src/prodik/application/update_edge_weight/increment_edge_weight.py rename to src/conode/application/update_edge_weight/increment_edge_weight.py index bf88b8e..39ee4f1 100644 --- a/src/prodik/application/update_edge_weight/increment_edge_weight.py +++ b/src/conode/application/update_edge_weight/increment_edge_weight.py @@ -1,13 +1,13 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.edge import EdgeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.edge import EdgeId @dataclass diff --git a/src/prodik/application/update_edge_weight/update_edge_weight.py b/src/conode/application/update_edge_weight/update_edge_weight.py similarity index 81% rename from src/prodik/application/update_edge_weight/update_edge_weight.py rename to src/conode/application/update_edge_weight/update_edge_weight.py index e51945b..d4bb09d 100644 --- a/src/prodik/application/update_edge_weight/update_edge_weight.py +++ b/src/conode/application/update_edge_weight/update_edge_weight.py @@ -1,13 +1,13 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.edge import EdgeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.edge import EdgeId @dataclass diff --git a/src/prodik/application/update_node/__init__.py b/src/conode/application/update_node/__init__.py similarity index 100% rename from src/prodik/application/update_node/__init__.py rename to src/conode/application/update_node/__init__.py diff --git a/src/prodik/application/update_node/update_node.py b/src/conode/application/update_node/update_node.py similarity index 86% rename from src/prodik/application/update_node/update_node.py rename to src/conode/application/update_node/update_node.py index 1ec73e3..23ab72f 100644 --- a/src/prodik/application/update_node/update_node.py +++ b/src/conode/application/update_node/update_node.py @@ -1,14 +1,14 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, GroupRepository, NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.node import NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.node import NodeId @dataclass(frozen=True, slots=True, kw_only=True) diff --git a/src/prodik/application/verify_company/__init__.py b/src/conode/application/verify_company/__init__.py similarity index 100% rename from src/prodik/application/verify_company/__init__.py rename to src/conode/application/verify_company/__init__.py diff --git a/src/prodik/application/verify_company/verify_company.py b/src/conode/application/verify_company/verify_company.py similarity index 79% rename from src/prodik/application/verify_company/verify_company.py rename to src/conode/application/verify_company/verify_company.py index 88f4384..601bf9a 100644 --- a/src/prodik/application/verify_company/verify_company.py +++ b/src/conode/application/verify_company/verify_company.py @@ -1,13 +1,13 @@ from dataclasses import dataclass -from prodik.application.errors import ( +from conode.application.errors import ( CompanyNotFoundError, NotEnoughRightsError, ) -from prodik.application.interfaces.repositories import CompanyRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService -from prodik.domain.company import CompanyId +from conode.application.interfaces.repositories import CompanyRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import AccessControlService +from conode.domain.company import CompanyId @dataclass diff --git a/src/prodik/bootstrap/__init__.py b/src/conode/bootstrap/__init__.py similarity index 100% rename from src/prodik/bootstrap/__init__.py rename to src/conode/bootstrap/__init__.py diff --git a/src/prodik/bootstrap/api/__init__.py b/src/conode/bootstrap/api/__init__.py similarity index 100% rename from src/prodik/bootstrap/api/__init__.py rename to src/conode/bootstrap/api/__init__.py diff --git a/src/prodik/bootstrap/api/run.py b/src/conode/bootstrap/api/run.py similarity index 87% rename from src/prodik/bootstrap/api/run.py rename to src/conode/bootstrap/api/run.py index 9113187..10eeead 100644 --- a/src/prodik/bootstrap/api/run.py +++ b/src/conode/bootstrap/api/run.py @@ -6,10 +6,10 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from prodik.bootstrap.di import get_async_container -from prodik.bootstrap.logs import configure_structlog -from prodik.infrastructure.config import Config, load_config -from prodik.presentation.common import ( +from conode.bootstrap.di import get_async_container +from conode.bootstrap.logs import configure_structlog +from conode.infrastructure.config import Config, load_config +from conode.presentation.common import ( include_exception_handlers, include_handlers, include_middlewares, diff --git a/src/prodik/bootstrap/cli.py b/src/conode/bootstrap/cli.py similarity index 88% rename from src/prodik/bootstrap/cli.py rename to src/conode/bootstrap/cli.py index 674dacf..e04a44b 100644 --- a/src/prodik/bootstrap/cli.py +++ b/src/conode/bootstrap/cli.py @@ -6,13 +6,13 @@ import alembic.config -import prodik.infrastructure.persistence -from prodik.bootstrap.api import run_http -from prodik.infrastructure.persistence import start_mapper +import conode.infrastructure.persistence +from conode.bootstrap.api import run_http +from conode.infrastructure.persistence import start_mapper def get_alembic_config_path() -> Iterator[Path]: - source = files(prodik.infrastructure.persistence).joinpath("alembic.ini") + source = files(conode.infrastructure.persistence).joinpath("alembic.ini") with as_file(source) as path: yield path @@ -69,9 +69,6 @@ def main() -> None: parser = configure_argument_parser() args = parser.parse_args() - if args.module == "api": - run_migrations() - start_mapper() args.func(args) diff --git a/src/prodik/bootstrap/di/__init__.py b/src/conode/bootstrap/di/__init__.py similarity index 100% rename from src/prodik/bootstrap/di/__init__.py rename to src/conode/bootstrap/di/__init__.py diff --git a/src/prodik/bootstrap/di/container.py b/src/conode/bootstrap/di/container.py similarity index 75% rename from src/prodik/bootstrap/di/container.py rename to src/conode/bootstrap/di/container.py index c75a4a4..f0c4ff2 100644 --- a/src/prodik/bootstrap/di/container.py +++ b/src/conode/bootstrap/di/container.py @@ -1,15 +1,14 @@ from dishka import AsyncContainer, make_async_container from dishka.integrations.fastapi import FastapiProvider -from prodik.bootstrap.di.providers import ( +from conode.bootstrap.di.providers import ( ApplicationProvider, - CacheConnectionProvider, ConnectionProvider, InfrastructureProvider, + SecretsProvider, ) -from prodik.infrastructure.config import ( +from conode.infrastructure.config import ( APIConfig, - CacheConfig, Config, DatabaseConfig, SecretsConfig, @@ -21,12 +20,11 @@ def get_async_container(config: Config) -> AsyncContainer: FastapiProvider(), InfrastructureProvider(), ApplicationProvider(), - CacheConnectionProvider(), + SecretsProvider(), ConnectionProvider(), context={ APIConfig: config.api, DatabaseConfig: config.database, SecretsConfig: config.secrets, - CacheConfig: config.cache, }, ) diff --git a/src/prodik/bootstrap/di/providers/__init__.py b/src/conode/bootstrap/di/providers/__init__.py similarity index 76% rename from src/prodik/bootstrap/di/providers/__init__.py rename to src/conode/bootstrap/di/providers/__init__.py index a91ffb0..6c35744 100644 --- a/src/prodik/bootstrap/di/providers/__init__.py +++ b/src/conode/bootstrap/di/providers/__init__.py @@ -1,11 +1,11 @@ from .application import ApplicationProvider -from .cache import CacheConnectionProvider from .connection import ConnectionProvider from .infrastructure import InfrastructureProvider +from .secrets import SecretsProvider __all__ = ( "ApplicationProvider", - "CacheConnectionProvider", "ConnectionProvider", "InfrastructureProvider", + "SecretsProvider", ) diff --git a/src/prodik/bootstrap/di/providers/application.py b/src/conode/bootstrap/di/providers/application.py similarity index 60% rename from src/prodik/bootstrap/di/providers/application.py rename to src/conode/bootstrap/di/providers/application.py index 7f2ac56..36f4816 100644 --- a/src/prodik/bootstrap/di/providers/application.py +++ b/src/conode/bootstrap/di/providers/application.py @@ -1,88 +1,78 @@ from dishka import Provider, Scope, provide_all -from prodik.application.attach_node_to_group import ( +from conode.application.attach_node_to_group import ( AttachNodeInteractor, ) -from prodik.application.authorization import ( - LoginInteractor, - RefreshTokenInteractor, - RegisterInteractor, -) -from prodik.application.detach_node_from_group import ( +from conode.application.detach_node_from_group import ( DetachNodeInteractor, ) -from prodik.application.manage_context import ( +from conode.application.manage_context import ( CreateContextInteractor, DeleteContextInteractor, ) -from prodik.application.manage_credentials import UpdateCurrentUserPasswordInteractor -from prodik.application.manage_edge import CreateEdgeInteractor, DeleteEdgeInteractor -from prodik.application.manage_group import CreateGroupInteractor, DeleteGroupInteractor -from prodik.application.manage_node import CreateNodeInteractor, DeleteNodeInteractor -from prodik.application.manage_profile import UpdateCurrentUserProfileInteractor -from prodik.application.manage_role import ( +from conode.application.manage_edge import CreateEdgeInteractor, DeleteEdgeInteractor +from conode.application.manage_group import CreateGroupInteractor, DeleteGroupInteractor +from conode.application.manage_node import CreateNodeInteractor, DeleteNodeInteractor +from conode.application.manage_profile import UpdateCurrentUserProfileInteractor +from conode.application.manage_role import ( CreateRoleInteractor, DeleteRoleInteractor, UpdateRoleInteractor, ) -from prodik.application.manage_user_rights import ( +from conode.application.manage_user_rights import ( GiveRoleToUserInteractor, RevokeRoleFromUserInteractor, ) -from prodik.application.receive_context_info import ( +from conode.application.receive_context_info import ( GetContextByIdInteractor, ) -from prodik.application.receive_graph_statistics import ( +from conode.application.receive_graph_statistics import ( FindShortestPathInteractor, GetNodeNeighboursInteractor, ) -from prodik.application.receive_group_info import GetGroupByIdInteractor -from prodik.application.receive_node_info import GetNodesByGroupInteractor -from prodik.application.receive_user_info import ( +from conode.application.receive_group_info import GetGroupByIdInteractor +from conode.application.receive_node_info import GetNodesByGroupInteractor +from conode.application.receive_user_info import ( GetCurrentUserInteractor, GetUserByUsernameInteractor, ) -from prodik.application.register_company import ( +from conode.application.register_company import ( RegisterCompanyInteractor, ) -from prodik.application.services import ( +from conode.application.services import ( AccessControlService, OfferAcceptanceService, OfferSendingService, RoleManagmentService, ) -from prodik.application.share_graph import ( +from conode.application.share_graph import ( AcceptOfferInteractor, DeclineOfferInteractor, SendOfferToCompanyInteractor, ) -from prodik.application.update_edge_weight import ( +from conode.application.update_edge_weight import ( DecrementEdgeWeightInteractor, IncrementEdgeWeightInteractor, UpdateEdgeWeightInteractor, ) -from prodik.application.update_node import ( +from conode.application.update_node import ( UpdateNodeInteractor, ) -from prodik.application.verify_company import VerifyCompanyInteractor +from conode.application.verify_company import VerifyCompanyInteractor class ApplicationProvider(Provider): provides = provide_all( UpdateNodeInteractor, - RegisterInteractor, - LoginInteractor, AccessControlService, OfferAcceptanceService, OfferSendingService, DeleteEdgeInteractor, - RefreshTokenInteractor, UpdateEdgeWeightInteractor, IncrementEdgeWeightInteractor, GetGroupByIdInteractor, GetContextByIdInteractor, UpdateCurrentUserProfileInteractor, - UpdateCurrentUserPasswordInteractor, SendOfferToCompanyInteractor, DecrementEdgeWeightInteractor, GetUserByUsernameInteractor, diff --git a/src/prodik/bootstrap/di/providers/connection.py b/src/conode/bootstrap/di/providers/connection.py similarity index 73% rename from src/prodik/bootstrap/di/providers/connection.py rename to src/conode/bootstrap/di/providers/connection.py index 4d18a4f..094a790 100644 --- a/src/prodik/bootstrap/di/providers/connection.py +++ b/src/conode/bootstrap/di/providers/connection.py @@ -1,7 +1,6 @@ from collections.abc import AsyncIterator from dishka import Provider, Scope, WithParents, provide, provide_all -from redis.asyncio import Redis from sqlalchemy import URL from sqlalchemy.ext.asyncio import ( AsyncSession, @@ -9,8 +8,8 @@ create_async_engine, ) -from prodik.infrastructure.config import CacheConfig, DatabaseConfig -from prodik.infrastructure.transaction_manager import TransactionManagerImpl +from conode.infrastructure.config import DatabaseConfig +from conode.infrastructure.transaction_manager import TransactionManagerImpl class ConnectionProvider(Provider): @@ -46,16 +45,3 @@ async def provide_async_session( ) -> AsyncIterator[AsyncSession]: async with session_factory() as session: yield session - - @provide(scope=Scope.REQUEST) - async def provide_redis_connection( - self, - config: CacheConfig, - ) -> AsyncIterator[Redis]: - client = Redis( - host=config.host, - port=config.port, - decode_responses=True, - ) - yield client - await client.aclose() diff --git a/src/prodik/bootstrap/di/providers/infrastructure.py b/src/conode/bootstrap/di/providers/infrastructure.py similarity index 71% rename from src/prodik/bootstrap/di/providers/infrastructure.py rename to src/conode/bootstrap/di/providers/infrastructure.py index 58d4948..4ad41a3 100644 --- a/src/prodik/bootstrap/di/providers/infrastructure.py +++ b/src/conode/bootstrap/di/providers/infrastructure.py @@ -1,14 +1,13 @@ from dishka import Provider, Scope, WithParents, provide_all -from prodik.infrastructure.identity_provider import IdentityProviderImpl -from prodik.infrastructure.password_hasher import PasswordHasherImpl -from prodik.infrastructure.repositories import ( +from conode.infrastructure.identity_provider import IdentityProviderImpl +from conode.infrastructure.password_hasher import PasswordHasherImpl +from conode.infrastructure.repositories import ( CompanyRepositoryImpl, ContextRepositoryImpl, ContractRepositoryImpl, EdgeRepositoryImpl, GroupRepositoryImpl, - LocalAuthorizationRepositoryImpl, NodeAssociationRepositoryImpl, NodeRepositoryImpl, OfferContextRepositoryImpl, @@ -17,14 +16,9 @@ OfferRepositoryImpl, RolePermissionsRepositoryImpl, RoleRepositoryImpl, - SessionRepositoryImpl, UserGrantRepositoryImpl, UserRepositoryImpl, ) -from prodik.infrastructure.token_managers import ( - AccessTokenManagerImpl, - RefreshTokenManagerImpl, -) class InfrastructureProvider(Provider): @@ -32,11 +26,7 @@ class InfrastructureProvider(Provider): WithParents[IdentityProviderImpl], WithParents[GroupRepositoryImpl], WithParents[PasswordHasherImpl], - WithParents[AccessTokenManagerImpl], - WithParents[RefreshTokenManagerImpl], WithParents[NodeAssociationRepositoryImpl], - WithParents[LocalAuthorizationRepositoryImpl], - WithParents[SessionRepositoryImpl], WithParents[CompanyRepositoryImpl], WithParents[UserRepositoryImpl], WithParents[NodeRepositoryImpl], diff --git a/src/conode/bootstrap/di/providers/secrets.py b/src/conode/bootstrap/di/providers/secrets.py new file mode 100644 index 0000000..76a11e6 --- /dev/null +++ b/src/conode/bootstrap/di/providers/secrets.py @@ -0,0 +1,10 @@ +from dishka import Provider, Scope, WithParents, provide_all + +from conode.infrastructure.token_manager import TokenManagerImpl + + +class SecretsProvider(Provider): + provides = provide_all( + WithParents[TokenManagerImpl], + scope=Scope.REQUEST, + ) diff --git a/src/prodik/bootstrap/logs.py b/src/conode/bootstrap/logs.py similarity index 100% rename from src/prodik/bootstrap/logs.py rename to src/conode/bootstrap/logs.py diff --git a/src/prodik/domain/__init__.py b/src/conode/domain/__init__.py similarity index 100% rename from src/prodik/domain/__init__.py rename to src/conode/domain/__init__.py diff --git a/src/prodik/domain/company/__init__.py b/src/conode/domain/company/__init__.py similarity index 100% rename from src/prodik/domain/company/__init__.py rename to src/conode/domain/company/__init__.py diff --git a/src/prodik/domain/company/errors.py b/src/conode/domain/company/errors.py similarity index 79% rename from src/prodik/domain/company/errors.py rename to src/conode/domain/company/errors.py index 9b24dd3..17f9b5b 100644 --- a/src/prodik/domain/company/errors.py +++ b/src/conode/domain/company/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class CompanyDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/company/model.py b/src/conode/domain/company/model.py similarity index 94% rename from src/prodik/domain/company/model.py rename to src/conode/domain/company/model.py index c8b93c0..57306fb 100644 --- a/src/prodik/domain/company/model.py +++ b/src/conode/domain/company/model.py @@ -3,12 +3,12 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company.errors import ( +from conode.domain.company.errors import ( InvalidCompanyDescriptionFormatError, InvalidCompanyNameFormatError, ) -from prodik.domain.shared import Entity, ValueObject -from prodik.domain.user import User, UserId +from conode.domain.shared import Entity, ValueObject +from conode.domain.user import User, UserId CompanyId = NewType("CompanyId", UUID) diff --git a/src/prodik/domain/context/__init__.py b/src/conode/domain/context/__init__.py similarity index 100% rename from src/prodik/domain/context/__init__.py rename to src/conode/domain/context/__init__.py diff --git a/src/prodik/domain/context/errors.py b/src/conode/domain/context/errors.py similarity index 79% rename from src/prodik/domain/context/errors.py rename to src/conode/domain/context/errors.py index 2661e1d..669be44 100644 --- a/src/prodik/domain/context/errors.py +++ b/src/conode/domain/context/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class ContextDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/context/model.py b/src/conode/domain/context/model.py similarity index 92% rename from src/prodik/domain/context/model.py rename to src/conode/domain/context/model.py index a770bda..b37e70c 100644 --- a/src/prodik/domain/context/model.py +++ b/src/conode/domain/context/model.py @@ -3,12 +3,12 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.context.errors import ( +from conode.domain.company import Company, CompanyId +from conode.domain.context.errors import ( InvalidContextDescriptionFormatError, InvalidContextNameFormatError, ) -from prodik.domain.shared import Entity, ValueObject +from conode.domain.shared import Entity, ValueObject ContextId = NewType("ContextId", UUID) diff --git a/src/prodik/domain/contract/__init__.py b/src/conode/domain/contract/__init__.py similarity index 100% rename from src/prodik/domain/contract/__init__.py rename to src/conode/domain/contract/__init__.py diff --git a/src/prodik/domain/contract/errors.py b/src/conode/domain/contract/errors.py similarity index 71% rename from src/prodik/domain/contract/errors.py rename to src/conode/domain/contract/errors.py index 6ceb02e..2a47081 100644 --- a/src/prodik/domain/contract/errors.py +++ b/src/conode/domain/contract/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class ContractDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/contract/model.py b/src/conode/domain/contract/model.py similarity index 89% rename from src/prodik/domain/contract/model.py rename to src/conode/domain/contract/model.py index 6a59b28..0ec5721 100644 --- a/src/prodik/domain/contract/model.py +++ b/src/conode/domain/contract/model.py @@ -4,11 +4,11 @@ from typing import NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.contract.errors import InvalidCompanyOffersFormatError -from prodik.domain.offer import Offer, OfferId -from prodik.domain.role import RoleId -from prodik.domain.shared import Entity +from conode.domain.company import Company, CompanyId +from conode.domain.contract.errors import InvalidCompanyOffersFormatError +from conode.domain.offer import Offer, OfferId +from conode.domain.role import RoleId +from conode.domain.shared import Entity ContractId = NewType("ContractId", UUID) diff --git a/src/prodik/domain/edge/__init__.py b/src/conode/domain/edge/__init__.py similarity index 100% rename from src/prodik/domain/edge/__init__.py rename to src/conode/domain/edge/__init__.py diff --git a/src/prodik/domain/edge/errors.py b/src/conode/domain/edge/errors.py similarity index 78% rename from src/prodik/domain/edge/errors.py rename to src/conode/domain/edge/errors.py index 8212a1a..60bde6d 100644 --- a/src/prodik/domain/edge/errors.py +++ b/src/conode/domain/edge/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class EdgeDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/edge/model.py b/src/conode/domain/edge/model.py similarity index 89% rename from src/prodik/domain/edge/model.py rename to src/conode/domain/edge/model.py index d4af854..23abb90 100644 --- a/src/prodik/domain/edge/model.py +++ b/src/conode/domain/edge/model.py @@ -3,14 +3,14 @@ from typing import NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.context import Context, ContextId -from prodik.domain.edge.errors import ( +from conode.domain.company import Company, CompanyId +from conode.domain.context import Context, ContextId +from conode.domain.edge.errors import ( EdgeCannotConnectTwoSameNodesError, EdgeWeightCannotBeNegativeError, ) -from prodik.domain.node import Node, NodeId -from prodik.domain.shared import Entity +from conode.domain.node import Node, NodeId +from conode.domain.shared import Entity EdgeId = NewType("EdgeId", UUID) diff --git a/src/prodik/domain/grant/__init__.py b/src/conode/domain/grant/__init__.py similarity index 100% rename from src/prodik/domain/grant/__init__.py rename to src/conode/domain/grant/__init__.py diff --git a/src/prodik/domain/grant/company.py b/src/conode/domain/grant/company.py similarity index 82% rename from src/prodik/domain/grant/company.py rename to src/conode/domain/grant/company.py index cd90359..ba4189e 100644 --- a/src/prodik/domain/grant/company.py +++ b/src/conode/domain/grant/company.py @@ -3,9 +3,9 @@ from typing import NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.role import Role, RoleId -from prodik.domain.shared import Entity +from conode.domain.company import Company, CompanyId +from conode.domain.role import Role, RoleId +from conode.domain.shared import Entity CompanyGrantId = NewType("CompanyGrantId", UUID) diff --git a/src/prodik/domain/grant/user.py b/src/conode/domain/grant/user.py similarity index 81% rename from src/prodik/domain/grant/user.py rename to src/conode/domain/grant/user.py index 6aad633..a2bc606 100644 --- a/src/prodik/domain/grant/user.py +++ b/src/conode/domain/grant/user.py @@ -3,9 +3,9 @@ from typing import NewType, Self from uuid import UUID -from prodik.domain.role import Role, RoleId -from prodik.domain.shared import Entity -from prodik.domain.user import User, UserId +from conode.domain.role import Role, RoleId +from conode.domain.shared import Entity +from conode.domain.user import User, UserId UserGrantId = NewType("UserGrantId", UUID) diff --git a/src/prodik/domain/group/__init__.py b/src/conode/domain/group/__init__.py similarity index 100% rename from src/prodik/domain/group/__init__.py rename to src/conode/domain/group/__init__.py diff --git a/src/prodik/domain/group/errors.py b/src/conode/domain/group/errors.py similarity index 83% rename from src/prodik/domain/group/errors.py rename to src/conode/domain/group/errors.py index 667be70..6c332da 100644 --- a/src/prodik/domain/group/errors.py +++ b/src/conode/domain/group/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class GroupDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/group/model.py b/src/conode/domain/group/model.py similarity index 94% rename from src/prodik/domain/group/model.py rename to src/conode/domain/group/model.py index a3d5ca2..b5adc30 100644 --- a/src/prodik/domain/group/model.py +++ b/src/conode/domain/group/model.py @@ -3,13 +3,13 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.group.errors import ( +from conode.domain.company import Company, CompanyId +from conode.domain.group.errors import ( GroupCannotInheritedFromItselfError, InvalidGroupDescriptionFormatError, InvalidGroupNameFormatError, ) -from prodik.domain.shared import Entity, ValueObject +from conode.domain.shared import Entity, ValueObject GroupId = NewType("GroupId", UUID) diff --git a/src/prodik/domain/node/__init__.py b/src/conode/domain/node/__init__.py similarity index 100% rename from src/prodik/domain/node/__init__.py rename to src/conode/domain/node/__init__.py diff --git a/src/prodik/domain/node/errors.py b/src/conode/domain/node/errors.py similarity index 78% rename from src/prodik/domain/node/errors.py rename to src/conode/domain/node/errors.py index 37cae77..733445d 100644 --- a/src/prodik/domain/node/errors.py +++ b/src/conode/domain/node/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class NodeDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/node/model.py b/src/conode/domain/node/model.py similarity index 93% rename from src/prodik/domain/node/model.py rename to src/conode/domain/node/model.py index 92e0fcd..28d9eea 100644 --- a/src/prodik/domain/node/model.py +++ b/src/conode/domain/node/model.py @@ -3,13 +3,13 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.group import Group, GroupId -from prodik.domain.node.errors import ( +from conode.domain.company import Company, CompanyId +from conode.domain.group import Group, GroupId +from conode.domain.node.errors import ( InvalidNodeDescriptionFormatError, InvalidNodeNameFormatError, ) -from prodik.domain.shared import Entity, ValueObject +from conode.domain.shared import Entity, ValueObject NodeId = NewType("NodeId", UUID) NodeAssociationId = NewType("NodeAssociationId", UUID) diff --git a/src/prodik/domain/offer/__init__.py b/src/conode/domain/offer/__init__.py similarity index 100% rename from src/prodik/domain/offer/__init__.py rename to src/conode/domain/offer/__init__.py diff --git a/src/prodik/domain/offer/errors.py b/src/conode/domain/offer/errors.py similarity index 89% rename from src/prodik/domain/offer/errors.py rename to src/conode/domain/offer/errors.py index 55689c8..1e7d6c8 100644 --- a/src/prodik/domain/offer/errors.py +++ b/src/conode/domain/offer/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class OfferDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/offer/model.py b/src/conode/domain/offer/model.py similarity index 95% rename from src/prodik/domain/offer/model.py rename to src/conode/domain/offer/model.py index 3142689..2c4701c 100644 --- a/src/prodik/domain/offer/model.py +++ b/src/conode/domain/offer/model.py @@ -4,10 +4,10 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.context import Context, ContextId -from prodik.domain.group import Group, GroupId -from prodik.domain.offer.errors import ( +from conode.domain.company import Company, CompanyId +from conode.domain.context import Context, ContextId +from conode.domain.group import Group, GroupId +from conode.domain.offer.errors import ( InvalidOfferDescriptionFormatError, InvalidOfferTitleFormatError, OfferCannotAnswerToItselfError, @@ -15,8 +15,8 @@ OfferCannotExpireAtPastError, OfferIsNotCounterOfferError, ) -from prodik.domain.role import PermissionType -from prodik.domain.shared import Entity, ValueObject +from conode.domain.role import PermissionType +from conode.domain.shared import Entity, ValueObject OfferId = NewType("OfferId", UUID) OfferLinkId = NewType("OfferLinkId", UUID) diff --git a/src/prodik/domain/role/__init__.py b/src/conode/domain/role/__init__.py similarity index 100% rename from src/prodik/domain/role/__init__.py rename to src/conode/domain/role/__init__.py diff --git a/src/prodik/domain/role/errors.py b/src/conode/domain/role/errors.py similarity index 69% rename from src/prodik/domain/role/errors.py rename to src/conode/domain/role/errors.py index 5bb2879..398935e 100644 --- a/src/prodik/domain/role/errors.py +++ b/src/conode/domain/role/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class RoleDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/role/model.py b/src/conode/domain/role/model.py similarity index 91% rename from src/prodik/domain/role/model.py rename to src/conode/domain/role/model.py index eae6147..7305c3d 100644 --- a/src/prodik/domain/role/model.py +++ b/src/conode/domain/role/model.py @@ -4,11 +4,11 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.company import Company, CompanyId -from prodik.domain.context import ContextId -from prodik.domain.group import GroupId -from prodik.domain.role.errors import InvalidRoleNameFormatError -from prodik.domain.shared import Entity, ValueObject +from conode.domain.company import Company, CompanyId +from conode.domain.context import ContextId +from conode.domain.group import GroupId +from conode.domain.role.errors import InvalidRoleNameFormatError +from conode.domain.shared import Entity, ValueObject RoleId = NewType("RoleId", UUID) RolePermissionId = NewType("RolePermissionId", UUID) diff --git a/src/prodik/domain/shared/__init__.py b/src/conode/domain/shared/__init__.py similarity index 100% rename from src/prodik/domain/shared/__init__.py rename to src/conode/domain/shared/__init__.py diff --git a/src/prodik/domain/shared/entity.py b/src/conode/domain/shared/entity.py similarity index 100% rename from src/prodik/domain/shared/entity.py rename to src/conode/domain/shared/entity.py diff --git a/src/prodik/domain/shared/value_object.py b/src/conode/domain/shared/value_object.py similarity index 100% rename from src/prodik/domain/shared/value_object.py rename to src/conode/domain/shared/value_object.py diff --git a/src/prodik/domain/user/__init__.py b/src/conode/domain/user/__init__.py similarity index 100% rename from src/prodik/domain/user/__init__.py rename to src/conode/domain/user/__init__.py diff --git a/src/prodik/domain/user/errors.py b/src/conode/domain/user/errors.py similarity index 86% rename from src/prodik/domain/user/errors.py rename to src/conode/domain/user/errors.py index 82c6786..3430605 100644 --- a/src/prodik/domain/user/errors.py +++ b/src/conode/domain/user/errors.py @@ -1,4 +1,4 @@ -from prodik.application.errors import ApplicationError +from conode.application.errors import ApplicationError class UserDomainValidationError(ApplicationError): ... diff --git a/src/prodik/domain/user/model.py b/src/conode/domain/user/model.py similarity index 93% rename from src/prodik/domain/user/model.py rename to src/conode/domain/user/model.py index d96d544..226ea83 100644 --- a/src/prodik/domain/user/model.py +++ b/src/conode/domain/user/model.py @@ -5,8 +5,8 @@ from typing import Final, NewType, Self from uuid import UUID -from prodik.domain.shared import Entity, ValueObject -from prodik.domain.user.errors import ( +from conode.domain.shared import Entity, ValueObject +from conode.domain.user.errors import ( InvalidUserEmailFormatError, InvalidUserFirstNameFormatError, InvalidUserLastNameFormatError, @@ -117,7 +117,6 @@ class User(Entity[UserId]): username: Username email: Email bio: Bio - token_revision: int @classmethod def new( @@ -139,7 +138,6 @@ def new( system_role=UserSystemRole.USER, created_at=now, updated_at=now, - token_revision=1, bio=Bio(bio), ) @@ -150,16 +148,11 @@ def update_profile( last_name: str, username: str, bio: str, - email: str, ) -> None: self.first_name = FirstName(first_name) self.last_name = LastName(last_name) self.username = Username(username) - self.email = Email(email) self.bio = Bio(bio) - def increment_revision(self) -> None: - self.token_revision += 1 - def is_admin(self) -> bool: return self.system_role == UserSystemRole.ADMIN diff --git a/src/prodik/infrastructure/__init__.py b/src/conode/infrastructure/__init__.py similarity index 100% rename from src/prodik/infrastructure/__init__.py rename to src/conode/infrastructure/__init__.py diff --git a/src/prodik/infrastructure/config.py b/src/conode/infrastructure/config.py similarity index 82% rename from src/prodik/infrastructure/config.py rename to src/conode/infrastructure/config.py index 6d634a3..48b08d2 100644 --- a/src/prodik/infrastructure/config.py +++ b/src/conode/infrastructure/config.py @@ -16,13 +16,9 @@ class APIConfig: class SecretsConfig: secret: str expires_in: int - - -@dataclass(slots=True, frozen=True, kw_only=True) -class CacheConfig: - host: str - port: int - ttl: int + jwks_url: str + audience: str + issuer: str @dataclass(slots=True, frozen=True, kw_only=True) @@ -39,7 +35,6 @@ class Config: api: APIConfig database: DatabaseConfig secrets: SecretsConfig - cache: CacheConfig def load_config(path: str = "config.toml") -> Config: @@ -62,10 +57,8 @@ def load_config(path: str = "config.toml") -> Config: secrets=SecretsConfig( secret=config["secrets"]["secret"], expires_in=config["secrets"]["expires_in"], - ), - cache=CacheConfig( - host=config["cache"]["host"], - port=config["cache"]["port"], - ttl=config["cache"]["ttl"], + jwks_url=config["secrets"]["jwks_url"], + audience=config["secrets"]["audience"], + issuer=config["secrets"]["issuer"], ), ) diff --git a/src/conode/infrastructure/identity_provider.py b/src/conode/infrastructure/identity_provider.py new file mode 100644 index 0000000..1386f8f --- /dev/null +++ b/src/conode/infrastructure/identity_provider.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass + +import structlog +from fastapi import Request +from jwt import PyJWTError + +from conode.application.errors import FailedToReadClientError, InvalidTokenError +from conode.application.interfaces.identity_provider import IdentityProvider +from conode.application.interfaces.token_manager import TokenManager, UserMeta + +logger = structlog.get_logger() + + +@dataclass +class IdentityProviderImpl(IdentityProvider): + request: Request + access_token_manager: TokenManager + + def get_current_ip(self) -> str: + if self.request.client is None: + raise FailedToReadClientError("Failed to read client error", None) + + return self.request.client.host + + def get_current_user_meta(self) -> UserMeta: + token = self.request.headers.get("X-Forwarded-Access-Token") + if token is None: + raise InvalidTokenError("Authorization token not found", None) + + try: + content = self.access_token_manager.decode(token) + except PyJWTError as e: + raise InvalidTokenError( + "Invalid authorization token", + [{"key": "Authorization", "value": token}], + ) from e + + return content diff --git a/src/prodik/infrastructure/password_hasher.py b/src/conode/infrastructure/password_hasher.py similarity index 89% rename from src/prodik/infrastructure/password_hasher.py rename to src/conode/infrastructure/password_hasher.py index 22a55cd..753a802 100644 --- a/src/prodik/infrastructure/password_hasher.py +++ b/src/conode/infrastructure/password_hasher.py @@ -3,7 +3,7 @@ from argon2 import PasswordHasher as Argon2Hasher from argon2.exceptions import Argon2Error, InvalidHashError -from prodik.application.interfaces.password_hasher import PasswordHasher +from conode.application.interfaces.password_hasher import PasswordHasher @dataclass diff --git a/src/prodik/infrastructure/persistence/__init__.py b/src/conode/infrastructure/persistence/__init__.py similarity index 100% rename from src/prodik/infrastructure/persistence/__init__.py rename to src/conode/infrastructure/persistence/__init__.py diff --git a/src/prodik/infrastructure/persistence/alembic.ini b/src/conode/infrastructure/persistence/alembic.ini similarity index 100% rename from src/prodik/infrastructure/persistence/alembic.ini rename to src/conode/infrastructure/persistence/alembic.ini diff --git a/src/prodik/infrastructure/persistence/migrations/README b/src/conode/infrastructure/persistence/migrations/README similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/README rename to src/conode/infrastructure/persistence/migrations/README diff --git a/src/prodik/infrastructure/persistence/migrations/__init__.py b/src/conode/infrastructure/persistence/migrations/__init__.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/__init__.py rename to src/conode/infrastructure/persistence/migrations/__init__.py diff --git a/src/prodik/infrastructure/persistence/migrations/env.py b/src/conode/infrastructure/persistence/migrations/env.py similarity index 96% rename from src/prodik/infrastructure/persistence/migrations/env.py rename to src/conode/infrastructure/persistence/migrations/env.py index f78aae0..e29df13 100644 --- a/src/prodik/infrastructure/persistence/migrations/env.py +++ b/src/conode/infrastructure/persistence/migrations/env.py @@ -5,8 +5,8 @@ from sqlalchemy import Connection, pool, URL from sqlalchemy.ext.asyncio import async_engine_from_config -from prodik.infrastructure.config import load_config -from prodik.infrastructure.persistence.registry import metadata +from conode.infrastructure.config import load_config +from conode.infrastructure.persistence.registry import metadata # this is the Alembic Config object, which provides # access to the values within the .ini file in use. diff --git a/src/prodik/infrastructure/persistence/migrations/script.py.mako b/src/conode/infrastructure/persistence/migrations/script.py.mako similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/script.py.mako rename to src/conode/infrastructure/persistence/migrations/script.py.mako diff --git a/src/prodik/infrastructure/persistence/migrations/versions/0333150881b1_parent_group_id.py b/src/conode/infrastructure/persistence/migrations/versions/0333150881b1_parent_group_id.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/0333150881b1_parent_group_id.py rename to src/conode/infrastructure/persistence/migrations/versions/0333150881b1_parent_group_id.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/080a7bac6dfb_custom_type.py b/src/conode/infrastructure/persistence/migrations/versions/080a7bac6dfb_custom_type.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/080a7bac6dfb_custom_type.py rename to src/conode/infrastructure/persistence/migrations/versions/080a7bac6dfb_custom_type.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/1786a4b3cea6_remove_company_id.py b/src/conode/infrastructure/persistence/migrations/versions/1786a4b3cea6_remove_company_id.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/1786a4b3cea6_remove_company_id.py rename to src/conode/infrastructure/persistence/migrations/versions/1786a4b3cea6_remove_company_id.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/2e3b8d977df1_offers_and_contracts.py b/src/conode/infrastructure/persistence/migrations/versions/2e3b8d977df1_offers_and_contracts.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/2e3b8d977df1_offers_and_contracts.py rename to src/conode/infrastructure/persistence/migrations/versions/2e3b8d977df1_offers_and_contracts.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/3057b93fb0cb_bio.py b/src/conode/infrastructure/persistence/migrations/versions/3057b93fb0cb_bio.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/3057b93fb0cb_bio.py rename to src/conode/infrastructure/persistence/migrations/versions/3057b93fb0cb_bio.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/33f30ca8ca69_company.py b/src/conode/infrastructure/persistence/migrations/versions/33f30ca8ca69_company.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/33f30ca8ca69_company.py rename to src/conode/infrastructure/persistence/migrations/versions/33f30ca8ca69_company.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/4a52d766f55e_nullable_from.py b/src/conode/infrastructure/persistence/migrations/versions/4a52d766f55e_nullable_from.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/4a52d766f55e_nullable_from.py rename to src/conode/infrastructure/persistence/migrations/versions/4a52d766f55e_nullable_from.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/4a8ee55b96fa_unique_constrait.py b/src/conode/infrastructure/persistence/migrations/versions/4a8ee55b96fa_unique_constrait.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/4a8ee55b96fa_unique_constrait.py rename to src/conode/infrastructure/persistence/migrations/versions/4a8ee55b96fa_unique_constrait.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/504f086cc1a2_permission_type_offer.py b/src/conode/infrastructure/persistence/migrations/versions/504f086cc1a2_permission_type_offer.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/504f086cc1a2_permission_type_offer.py rename to src/conode/infrastructure/persistence/migrations/versions/504f086cc1a2_permission_type_offer.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/5192c880eb4e_edge_unique_constrait.py b/src/conode/infrastructure/persistence/migrations/versions/5192c880eb4e_edge_unique_constrait.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/5192c880eb4e_edge_unique_constrait.py rename to src/conode/infrastructure/persistence/migrations/versions/5192c880eb4e_edge_unique_constrait.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/6240bee5cfae_remove_owner_id.py b/src/conode/infrastructure/persistence/migrations/versions/6240bee5cfae_remove_owner_id.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/6240bee5cfae_remove_owner_id.py rename to src/conode/infrastructure/persistence/migrations/versions/6240bee5cfae_remove_owner_id.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/658eaeeb4857_edge_company_id.py b/src/conode/infrastructure/persistence/migrations/versions/658eaeeb4857_edge_company_id.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/658eaeeb4857_edge_company_id.py rename to src/conode/infrastructure/persistence/migrations/versions/658eaeeb4857_edge_company_id.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/6c046693f2e1_renamed_hashed_password_name.py b/src/conode/infrastructure/persistence/migrations/versions/6c046693f2e1_renamed_hashed_password_name.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/6c046693f2e1_renamed_hashed_password_name.py rename to src/conode/infrastructure/persistence/migrations/versions/6c046693f2e1_renamed_hashed_password_name.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/6c7e8a84a7c8_grants.py b/src/conode/infrastructure/persistence/migrations/versions/6c7e8a84a7c8_grants.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/6c7e8a84a7c8_grants.py rename to src/conode/infrastructure/persistence/migrations/versions/6c7e8a84a7c8_grants.py diff --git a/src/conode/infrastructure/persistence/migrations/versions/74a91a825965_remove_old_authorization.py b/src/conode/infrastructure/persistence/migrations/versions/74a91a825965_remove_old_authorization.py new file mode 100644 index 0000000..bd89c7b --- /dev/null +++ b/src/conode/infrastructure/persistence/migrations/versions/74a91a825965_remove_old_authorization.py @@ -0,0 +1,53 @@ +"""remove-old-authorization + +Revision ID: 74a91a825965 +Revises: 5192c880eb4e +Create Date: 2026-07-19 16:59:23.174543 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = '74a91a825965' +down_revision: Union[str, Sequence[str], None] = '5192c880eb4e' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('local_authorization_record') + op.drop_table('session_record') + op.drop_column('user_record', 'token_revision') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user_record', sa.Column('token_revision', sa.INTEGER(), autoincrement=False, nullable=False)) + op.create_table('session_record', + sa.Column('id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('user_id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('host', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column('token', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user_record.id'], name=op.f('session_record_user_id_fkey'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('session_record_pkey')) + ) + op.create_table('local_authorization_record', + sa.Column('id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('user_id', sa.UUID(), autoincrement=False, nullable=False), + sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=False), + sa.Column('password', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user_record.id'], name=op.f('local_authorization_record_user_id_fkey'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('local_authorization_record_pkey')) + ) + # ### end Alembic commands ### diff --git a/src/prodik/infrastructure/persistence/migrations/versions/78521bfa1ec2_contexts_and_edges.py b/src/conode/infrastructure/persistence/migrations/versions/78521bfa1ec2_contexts_and_edges.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/78521bfa1ec2_contexts_and_edges.py rename to src/conode/infrastructure/persistence/migrations/versions/78521bfa1ec2_contexts_and_edges.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/88413fda6e9e_initial.py b/src/conode/infrastructure/persistence/migrations/versions/88413fda6e9e_initial.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/88413fda6e9e_initial.py rename to src/conode/infrastructure/persistence/migrations/versions/88413fda6e9e_initial.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/b5ff44a6d487_nodes_and_groups.py b/src/conode/infrastructure/persistence/migrations/versions/b5ff44a6d487_nodes_and_groups.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/b5ff44a6d487_nodes_and_groups.py rename to src/conode/infrastructure/persistence/migrations/versions/b5ff44a6d487_nodes_and_groups.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/d221025bd616_role_name.py b/src/conode/infrastructure/persistence/migrations/versions/d221025bd616_role_name.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/d221025bd616_role_name.py rename to src/conode/infrastructure/persistence/migrations/versions/d221025bd616_role_name.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/e60ec6d4a0a5_roles_and_permissions.py b/src/conode/infrastructure/persistence/migrations/versions/e60ec6d4a0a5_roles_and_permissions.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/e60ec6d4a0a5_roles_and_permissions.py rename to src/conode/infrastructure/persistence/migrations/versions/e60ec6d4a0a5_roles_and_permissions.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/ee772395dc0d_association_cascade.py b/src/conode/infrastructure/persistence/migrations/versions/ee772395dc0d_association_cascade.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/ee772395dc0d_association_cascade.py rename to src/conode/infrastructure/persistence/migrations/versions/ee772395dc0d_association_cascade.py diff --git a/src/prodik/infrastructure/persistence/migrations/versions/ee89ddc73033_contexts.py b/src/conode/infrastructure/persistence/migrations/versions/ee89ddc73033_contexts.py similarity index 100% rename from src/prodik/infrastructure/persistence/migrations/versions/ee89ddc73033_contexts.py rename to src/conode/infrastructure/persistence/migrations/versions/ee89ddc73033_contexts.py diff --git a/src/prodik/infrastructure/persistence/registry.py b/src/conode/infrastructure/persistence/registry.py similarity index 87% rename from src/prodik/infrastructure/persistence/registry.py rename to src/conode/infrastructure/persistence/registry.py index b124ccb..829f1fa 100644 --- a/src/prodik/infrastructure/persistence/registry.py +++ b/src/conode/infrastructure/persistence/registry.py @@ -6,23 +6,20 @@ Enum, Float, ForeignKey, - Integer, MetaData, - String, Table, UniqueConstraint, ) from sqlalchemy.orm import registry -from prodik.domain.authorization import LocalAuthorization, Session -from prodik.domain.company import Company -from prodik.domain.context import Context -from prodik.domain.contract import Contract, ContractStatus -from prodik.domain.edge import Edge -from prodik.domain.grant import CompanyGrant, UserGrant -from prodik.domain.group import Group -from prodik.domain.node import Node, NodeAssociation -from prodik.domain.offer import ( +from conode.domain.company import Company +from conode.domain.context import Context +from conode.domain.contract import Contract, ContractStatus +from conode.domain.edge import Edge +from conode.domain.grant import CompanyGrant, UserGrant +from conode.domain.group import Group +from conode.domain.node import Node, NodeAssociation +from conode.domain.offer import ( Offer, OfferContext, OfferGroup, @@ -30,9 +27,9 @@ OfferLinkStatus, OfferStatus, ) -from prodik.domain.role import EntityType, PermissionType, Role, RolePermission -from prodik.domain.user import User, UserSystemRole -from prodik.infrastructure.persistence.types import ( +from conode.domain.role import EntityType, PermissionType, Role, RolePermission +from conode.domain.user import User, UserSystemRole +from conode.infrastructure.persistence.types import ( BioType, CompanyDescriptionType, CompanyNameType, @@ -64,28 +61,6 @@ Column("first_name", FirstNameType, nullable=False), Column("email", EmailType, nullable=False), Column("bio", BioType, nullable=False), - Column("token_revision", Integer, nullable=False), - Column("created_at", DateTime(timezone=True), nullable=False), - Column("updated_at", DateTime(timezone=True), nullable=False), -) - -session_record_table = Table( - "session_record", - metadata, - Column("id", UUID, primary_key=True, nullable=False), - Column("user_id", ForeignKey("user_record.id", ondelete="CASCADE"), nullable=False), - Column("host", String, nullable=False), - Column("token", String, nullable=False), - Column("created_at", DateTime(timezone=True), nullable=False), - Column("updated_at", DateTime(timezone=True), nullable=False), -) - -local_authorization_record_table = Table( - "local_authorization_record", - metadata, - Column("id", UUID, primary_key=True, nullable=False), - Column("user_id", ForeignKey("user_record.id", ondelete="CASCADE"), nullable=False), - Column("password", String, nullable=False), Column("created_at", DateTime(timezone=True), nullable=False), Column("updated_at", DateTime(timezone=True), nullable=False), ) @@ -404,11 +379,6 @@ def start_mapper() -> None: registry_mapper.map_imperatively(User, user_record_table) - registry_mapper.map_imperatively(Session, session_record_table) - registry_mapper.map_imperatively( - LocalAuthorization, - local_authorization_record_table, - ) registry_mapper.map_imperatively(Company, company_record_table) registry_mapper.map_imperatively(Node, node_record_table) registry_mapper.map_imperatively(Group, group_record_table) diff --git a/src/prodik/infrastructure/persistence/types.py b/src/conode/infrastructure/persistence/types.py similarity index 84% rename from src/prodik/infrastructure/persistence/types.py rename to src/conode/infrastructure/persistence/types.py index c200a25..56aa9d0 100644 --- a/src/prodik/infrastructure/persistence/types.py +++ b/src/conode/infrastructure/persistence/types.py @@ -3,14 +3,14 @@ from sqlalchemy import Dialect, String from sqlalchemy.types import TypeDecorator -from prodik.domain.company import CompanyDescription, CompanyName -from prodik.domain.context import ContextDescription, ContextName -from prodik.domain.group import GroupDescription, GroupName -from prodik.domain.node import NodeDescription, NodeName -from prodik.domain.offer import OfferDescription, OfferTitle -from prodik.domain.role import RoleName -from prodik.domain.shared import ValueObject -from prodik.domain.user import Bio, Email, FirstName, LastName, Username +from conode.domain.company import CompanyDescription, CompanyName +from conode.domain.context import ContextDescription, ContextName +from conode.domain.group import GroupDescription, GroupName +from conode.domain.node import NodeDescription, NodeName +from conode.domain.offer import OfferDescription, OfferTitle +from conode.domain.role import RoleName +from conode.domain.shared import ValueObject +from conode.domain.user import Bio, Email, FirstName, LastName, Username class BaseVOTypeDecorator[T: ValueObject[Any]](TypeDecorator[T]): diff --git a/src/prodik/infrastructure/repositories/__init__.py b/src/conode/infrastructure/repositories/__init__.py similarity index 87% rename from src/prodik/infrastructure/repositories/__init__.py rename to src/conode/infrastructure/repositories/__init__.py index b989eb6..a0b0e75 100644 --- a/src/prodik/infrastructure/repositories/__init__.py +++ b/src/conode/infrastructure/repositories/__init__.py @@ -1,4 +1,3 @@ -from .authorization import LocalAuthorizationRepositoryImpl, SessionRepositoryImpl from .company import CompanyRepositoryImpl from .context import ContextRepositoryImpl from .contract import ContractRepositoryImpl @@ -22,7 +21,6 @@ "ContractRepositoryImpl", "EdgeRepositoryImpl", "GroupRepositoryImpl", - "LocalAuthorizationRepositoryImpl", "NodeAssociationRepositoryImpl", "NodeRepositoryImpl", "OfferContextRepositoryImpl", @@ -31,7 +29,6 @@ "OfferRepositoryImpl", "RolePermissionsRepositoryImpl", "RoleRepositoryImpl", - "SessionRepositoryImpl", "UserGrantRepositoryImpl", "UserRepositoryImpl", ) diff --git a/src/prodik/infrastructure/repositories/company.py b/src/conode/infrastructure/repositories/company.py similarity index 94% rename from src/prodik/infrastructure/repositories/company.py rename to src/conode/infrastructure/repositories/company.py index a9146aa..10e3bdf 100644 --- a/src/prodik/infrastructure/repositories/company.py +++ b/src/conode/infrastructure/repositories/company.py @@ -4,10 +4,10 @@ from sqlalchemy import insert, select, update from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import CompanyNotFoundError -from prodik.application.interfaces.repositories import CompanyRepository -from prodik.domain.company.model import Company, CompanyId, CompanyName -from prodik.domain.user import UserId +from conode.application.errors import CompanyNotFoundError +from conode.application.interfaces.repositories import CompanyRepository +from conode.domain.company.model import Company, CompanyId, CompanyName +from conode.domain.user import UserId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/context.py b/src/conode/infrastructure/repositories/context.py similarity index 92% rename from src/prodik/infrastructure/repositories/context.py rename to src/conode/infrastructure/repositories/context.py index 0635791..e05f04c 100644 --- a/src/prodik/infrastructure/repositories/context.py +++ b/src/conode/infrastructure/repositories/context.py @@ -4,10 +4,10 @@ from sqlalchemy import delete, insert, select from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import ContextNotFoundError -from prodik.application.interfaces.repositories import ContextRepository -from prodik.domain.company import CompanyId -from prodik.domain.context import Context, ContextId +from conode.application.errors import ContextNotFoundError +from conode.application.interfaces.repositories import ContextRepository +from conode.domain.company import CompanyId +from conode.domain.context import Context, ContextId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/contract.py b/src/conode/infrastructure/repositories/contract.py similarity index 90% rename from src/prodik/infrastructure/repositories/contract.py rename to src/conode/infrastructure/repositories/contract.py index f6b9620..bba1d91 100644 --- a/src/prodik/infrastructure/repositories/contract.py +++ b/src/conode/infrastructure/repositories/contract.py @@ -4,8 +4,8 @@ from sqlalchemy import insert from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.interfaces.repositories import ContractRepository -from prodik.domain.contract.model import Contract +from conode.application.interfaces.repositories import ContractRepository +from conode.domain.contract.model import Contract logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/edge.py b/src/conode/infrastructure/repositories/edge.py similarity index 94% rename from src/prodik/infrastructure/repositories/edge.py rename to src/conode/infrastructure/repositories/edge.py index c3b2451..a772781 100644 --- a/src/prodik/infrastructure/repositories/edge.py +++ b/src/conode/infrastructure/repositories/edge.py @@ -5,11 +5,11 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import EdgeAlreadyExistsError, EdgeNotFoundError -from prodik.application.interfaces.repositories import EdgeRepository -from prodik.domain.context import ContextId -from prodik.domain.edge import Edge, EdgeId -from prodik.domain.node import NodeId +from conode.application.errors import EdgeAlreadyExistsError, EdgeNotFoundError +from conode.application.interfaces.repositories import EdgeRepository +from conode.domain.context import ContextId +from conode.domain.edge import Edge, EdgeId +from conode.domain.node import NodeId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/grant/__init__.py b/src/conode/infrastructure/repositories/grant/__init__.py similarity index 100% rename from src/prodik/infrastructure/repositories/grant/__init__.py rename to src/conode/infrastructure/repositories/grant/__init__.py diff --git a/src/prodik/infrastructure/repositories/grant/user.py b/src/conode/infrastructure/repositories/grant/user.py similarity index 91% rename from src/prodik/infrastructure/repositories/grant/user.py rename to src/conode/infrastructure/repositories/grant/user.py index 1188f58..de6ac92 100644 --- a/src/prodik/infrastructure/repositories/grant/user.py +++ b/src/conode/infrastructure/repositories/grant/user.py @@ -4,10 +4,10 @@ from sqlalchemy import insert, select from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.interfaces.repositories import UserGrantRepository -from prodik.domain.grant import UserGrant -from prodik.domain.role import RoleId -from prodik.domain.user import UserId +from conode.application.interfaces.repositories import UserGrantRepository +from conode.domain.grant import UserGrant +from conode.domain.role import RoleId +from conode.domain.user import UserId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/group.py b/src/conode/infrastructure/repositories/group.py similarity index 92% rename from src/prodik/infrastructure/repositories/group.py rename to src/conode/infrastructure/repositories/group.py index 25310f8..69910b2 100644 --- a/src/prodik/infrastructure/repositories/group.py +++ b/src/conode/infrastructure/repositories/group.py @@ -4,10 +4,10 @@ from sqlalchemy import delete, insert, select from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import GroupNotFoundError -from prodik.application.interfaces.repositories import GroupRepository -from prodik.domain.company import CompanyId -from prodik.domain.group import Group, GroupId +from conode.application.errors import GroupNotFoundError +from conode.application.interfaces.repositories import GroupRepository +from conode.domain.company import CompanyId +from conode.domain.group import Group, GroupId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/node.py b/src/conode/infrastructure/repositories/node.py similarity index 97% rename from src/prodik/infrastructure/repositories/node.py rename to src/conode/infrastructure/repositories/node.py index a887d66..266eced 100644 --- a/src/prodik/infrastructure/repositories/node.py +++ b/src/conode/infrastructure/repositories/node.py @@ -5,19 +5,19 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import ( +from conode.application.errors import ( AssociationNotFoundError, NodeCannotHaveSameAssociationsError, NodeMustHaveAtLeastOneAssociationError, NodeNotFoundError, ) -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( NodeAssociationRepository, NodeRepository, ) -from prodik.domain.company import CompanyId -from prodik.domain.group import GroupId -from prodik.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId +from conode.domain.company import CompanyId +from conode.domain.group import GroupId +from conode.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/offer.py b/src/conode/infrastructure/repositories/offer.py similarity index 97% rename from src/prodik/infrastructure/repositories/offer.py rename to src/conode/infrastructure/repositories/offer.py index e9a0eeb..047a1c1 100644 --- a/src/prodik/infrastructure/repositories/offer.py +++ b/src/conode/infrastructure/repositories/offer.py @@ -4,14 +4,14 @@ from sqlalchemy import and_, insert, select, update from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import OfferNotFoundError -from prodik.application.interfaces.repositories import ( +from conode.application.errors import OfferNotFoundError +from conode.application.interfaces.repositories import ( OfferContextRepository, OfferGroupRepository, OfferLinkRepository, OfferRepository, ) -from prodik.domain.offer.model import ( +from conode.domain.offer.model import ( Offer, OfferContext, OfferGroup, diff --git a/src/prodik/infrastructure/repositories/permission.py b/src/conode/infrastructure/repositories/permission.py similarity index 95% rename from src/prodik/infrastructure/repositories/permission.py rename to src/conode/infrastructure/repositories/permission.py index f64c872..16034ec 100644 --- a/src/prodik/infrastructure/repositories/permission.py +++ b/src/conode/infrastructure/repositories/permission.py @@ -4,11 +4,11 @@ from sqlalchemy import insert, select, update from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( RolePermissionsRepository, ) -from prodik.domain.role import RolePermission -from prodik.domain.role.model import RoleId, RolePermissionId +from conode.domain.role import RolePermission +from conode.domain.role.model import RoleId, RolePermissionId logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/role.py b/src/conode/infrastructure/repositories/role.py similarity index 92% rename from src/prodik/infrastructure/repositories/role.py rename to src/conode/infrastructure/repositories/role.py index f9496a0..8efb168 100644 --- a/src/prodik/infrastructure/repositories/role.py +++ b/src/conode/infrastructure/repositories/role.py @@ -4,11 +4,11 @@ from sqlalchemy import and_, delete, insert, select, update from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import RoleNotFoundError -from prodik.application.interfaces.repositories import RoleRepository -from prodik.domain.company import CompanyId -from prodik.domain.role import Role -from prodik.domain.role.model import RoleId, RoleName +from conode.application.errors import RoleNotFoundError +from conode.application.interfaces.repositories import RoleRepository +from conode.domain.company import CompanyId +from conode.domain.role import Role +from conode.domain.role.model import RoleId, RoleName logger = structlog.get_logger() diff --git a/src/prodik/infrastructure/repositories/user.py b/src/conode/infrastructure/repositories/user.py similarity index 91% rename from src/prodik/infrastructure/repositories/user.py rename to src/conode/infrastructure/repositories/user.py index 198805c..3ef661c 100644 --- a/src/prodik/infrastructure/repositories/user.py +++ b/src/conode/infrastructure/repositories/user.py @@ -4,9 +4,9 @@ from sqlalchemy import insert, or_, select, update from sqlalchemy.ext.asyncio import AsyncSession -from prodik.application.errors import UserNotFoundError -from prodik.application.interfaces.repositories import UserRepository -from prodik.domain.user import Email, User, UserId, Username +from conode.application.errors import UserNotFoundError +from conode.application.interfaces.repositories import UserRepository +from conode.domain.user import Email, User, UserId, Username logger = structlog.get_logger() @@ -26,7 +26,6 @@ async def create(self, user: User) -> None: username=user.username, email=user.email, bio=user.bio, - token_revision=user.token_revision, created_at=user.created_at, updated_at=user.updated_at, ), @@ -44,9 +43,7 @@ async def update(self, user: User) -> None: first_name=user.first_name, last_name=user.last_name, username=user.username, - email=user.email, bio=user.bio, - token_revision=user.token_revision, ), ) diff --git a/src/conode/infrastructure/token_manager.py b/src/conode/infrastructure/token_manager.py new file mode 100644 index 0000000..ba26c2c --- /dev/null +++ b/src/conode/infrastructure/token_manager.py @@ -0,0 +1,39 @@ +import jwt + +from conode.application.interfaces.token_manager import ( + TokenManager, + UserMeta, +) +from conode.domain.user import UserSystemRole +from conode.domain.user.model import User +from conode.infrastructure.config import SecretsConfig + + +class TokenManagerImpl(TokenManager): + def __init__(self, config: SecretsConfig) -> None: + self._client = jwt.PyJWKClient(config.jwks_url) + self._config = config + + def encode(self, user: User) -> str: + raise NotImplementedError + + def decode(self, token: str) -> UserMeta: + signing_key = self._client.get_signing_key_from_jwt(token) + payload = jwt.decode( + token, + signing_key.key, + algorithms=["RS256"], + audience=self._config.audience, + issuer=self._config.issuer, + ) + role = UserSystemRole.USER + if "realm-admin" in payload["resource_access"]["realm-management"]["roles"]: + role = UserSystemRole.ADMIN + + return UserMeta( + email=payload["email"], + first_name=payload["given_name"], + last_name=payload["family_name"], + username=payload["preferred_username"], + system_role=role, + ) diff --git a/src/prodik/infrastructure/transaction_manager.py b/src/conode/infrastructure/transaction_manager.py similarity index 94% rename from src/prodik/infrastructure/transaction_manager.py rename to src/conode/infrastructure/transaction_manager.py index 98569e5..5814dce 100644 --- a/src/prodik/infrastructure/transaction_manager.py +++ b/src/conode/infrastructure/transaction_manager.py @@ -8,7 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from structlog.contextvars import bind_contextvars -from prodik.application.interfaces.transaction_manager import TransactionManager +from conode.application.interfaces.transaction_manager import TransactionManager logger = structlog.get_logger() diff --git a/src/prodik/presentation/__init__.py b/src/conode/presentation/__init__.py similarity index 100% rename from src/prodik/presentation/__init__.py rename to src/conode/presentation/__init__.py diff --git a/src/prodik/presentation/common.py b/src/conode/presentation/common.py similarity index 78% rename from src/prodik/presentation/common.py rename to src/conode/presentation/common.py index 524c1a9..dbf4319 100644 --- a/src/prodik/presentation/common.py +++ b/src/conode/presentation/common.py @@ -1,14 +1,13 @@ import structlog from fastapi import FastAPI -from prodik.application.errors import ApplicationError -from prodik.presentation.exceptions import ( +from conode.application.errors import ApplicationError +from conode.presentation.exceptions import ( application_error_handler, default_error_handler, ) -from prodik.presentation.middlewares import LoggerMiddleware -from prodik.presentation.views import ( - auth, +from conode.presentation.middlewares import LoggerMiddleware +from conode.presentation.views import ( company, context, edge, @@ -30,7 +29,6 @@ def include_handlers(app: FastAPI) -> None: app.include_router(offer) app.include_router(role) app.include_router(root) - app.include_router(auth) app.include_router(user) app.include_router(node) app.include_router(edge) diff --git a/src/prodik/presentation/exceptions/__init__.py b/src/conode/presentation/exceptions/__init__.py similarity index 100% rename from src/prodik/presentation/exceptions/__init__.py rename to src/conode/presentation/exceptions/__init__.py diff --git a/src/prodik/presentation/exceptions/handlers.py b/src/conode/presentation/exceptions/handlers.py similarity index 88% rename from src/prodik/presentation/exceptions/handlers.py rename to src/conode/presentation/exceptions/handlers.py index 06f74ac..c469a7f 100644 --- a/src/prodik/presentation/exceptions/handlers.py +++ b/src/conode/presentation/exceptions/handlers.py @@ -6,7 +6,7 @@ from fastapi.encoders import jsonable_encoder from fastapi.responses import JSONResponse -from prodik.application.errors import ( +from conode.application.errors import ( ApplicationError, AssociationNotFoundError, AuthorizationNotFoundError, @@ -35,13 +35,13 @@ UserAlreadyExistsError, UserNotFoundError, ) -from prodik.domain.company.errors import CompanyDomainValidationError -from prodik.domain.context.errors import ContextDomainValidationError -from prodik.domain.edge.errors import EdgeDomainValidationError -from prodik.domain.group.errors import GroupDomainValidationError -from prodik.domain.node.errors import NodeDomainValidationError -from prodik.domain.role.errors import RoleDomainValidationError -from prodik.domain.user.errors import UserDomainValidationError +from conode.domain.company.errors import CompanyDomainValidationError +from conode.domain.context.errors import ContextDomainValidationError +from conode.domain.edge.errors import EdgeDomainValidationError +from conode.domain.group.errors import GroupDomainValidationError +from conode.domain.node.errors import NodeDomainValidationError +from conode.domain.role.errors import RoleDomainValidationError +from conode.domain.user.errors import UserDomainValidationError logger = structlog.get_logger() diff --git a/src/prodik/presentation/middlewares/__init__.py b/src/conode/presentation/middlewares/__init__.py similarity index 100% rename from src/prodik/presentation/middlewares/__init__.py rename to src/conode/presentation/middlewares/__init__.py diff --git a/src/prodik/presentation/middlewares/log.py b/src/conode/presentation/middlewares/log.py similarity index 100% rename from src/prodik/presentation/middlewares/log.py rename to src/conode/presentation/middlewares/log.py diff --git a/src/prodik/presentation/schemas/__init__.py b/src/conode/presentation/schemas/__init__.py similarity index 100% rename from src/prodik/presentation/schemas/__init__.py rename to src/conode/presentation/schemas/__init__.py diff --git a/src/prodik/presentation/schemas/company.py b/src/conode/presentation/schemas/company.py similarity index 86% rename from src/prodik/presentation/schemas/company.py rename to src/conode/presentation/schemas/company.py index 2d31199..5470060 100644 --- a/src/prodik/presentation/schemas/company.py +++ b/src/conode/presentation/schemas/company.py @@ -2,14 +2,14 @@ from pydantic import BaseModel, Field -from prodik.domain.company import CompanyId -from prodik.domain.company.model import ( +from conode.domain.company import CompanyId +from conode.domain.company.model import ( MAX_ALLOWED_COMPANY_DESCRIPTION_LENGTH, MAX_ALLOWED_COMPANY_NAME_LENGTH, MIN_ALLOWED_COMPANY_DESCRIPTION_LENGTH, MIN_ALLOWED_COMPANY_NAME_LENGTH, ) -from prodik.domain.user import UserId +from conode.domain.user import UserId class CompanySchema(BaseModel): diff --git a/src/prodik/presentation/schemas/context.py b/src/conode/presentation/schemas/context.py similarity index 83% rename from src/prodik/presentation/schemas/context.py rename to src/conode/presentation/schemas/context.py index 6e05d78..fd4f9e9 100644 --- a/src/prodik/presentation/schemas/context.py +++ b/src/conode/presentation/schemas/context.py @@ -2,9 +2,9 @@ from pydantic import BaseModel, Field -from prodik.domain.company import CompanyId -from prodik.domain.context import ContextId -from prodik.domain.context.model import ( +from conode.domain.company import CompanyId +from conode.domain.context import ContextId +from conode.domain.context.model import ( MAX_ALLOWED_CONTEXT_DESCRIPTION_LENGTH, MAX_ALLOWED_CONTEXT_NAME_LENGTH, MIN_ALLOWED_CONTEXT_NAME_LENGTH, diff --git a/src/prodik/presentation/schemas/edge.py b/src/conode/presentation/schemas/edge.py similarity index 67% rename from src/prodik/presentation/schemas/edge.py rename to src/conode/presentation/schemas/edge.py index beda32e..69cf8d9 100644 --- a/src/prodik/presentation/schemas/edge.py +++ b/src/conode/presentation/schemas/edge.py @@ -1,9 +1,9 @@ from pydantic import BaseModel -from prodik.domain.company import CompanyId -from prodik.domain.context import ContextId -from prodik.domain.edge import EdgeId -from prodik.domain.node import NodeId +from conode.domain.company import CompanyId +from conode.domain.context import ContextId +from conode.domain.edge import EdgeId +from conode.domain.node import NodeId class CreateEdgeRequest(BaseModel): diff --git a/src/prodik/presentation/schemas/group.py b/src/conode/presentation/schemas/group.py similarity index 85% rename from src/prodik/presentation/schemas/group.py rename to src/conode/presentation/schemas/group.py index 927a744..47c3118 100644 --- a/src/prodik/presentation/schemas/group.py +++ b/src/conode/presentation/schemas/group.py @@ -2,9 +2,9 @@ from pydantic import BaseModel, Field -from prodik.domain.company import CompanyId -from prodik.domain.group import GroupId -from prodik.domain.group.model import ( +from conode.domain.company import CompanyId +from conode.domain.group import GroupId +from conode.domain.group.model import ( MAX_ALLOWED_GROUP_DESCRIPTION_LENGTH, MAX_ALLOWED_GROUP_NAME_LENGTH, MIN_ALLOWED_GROUP_NAME_LENGTH, diff --git a/src/prodik/presentation/schemas/node.py b/src/conode/presentation/schemas/node.py similarity index 86% rename from src/prodik/presentation/schemas/node.py rename to src/conode/presentation/schemas/node.py index 429b297..3ae4354 100644 --- a/src/prodik/presentation/schemas/node.py +++ b/src/conode/presentation/schemas/node.py @@ -2,10 +2,10 @@ from pydantic import BaseModel, Field -from prodik.domain.company import CompanyId -from prodik.domain.group import GroupId -from prodik.domain.node import NodeAssociationId, NodeId -from prodik.domain.node.model import ( +from conode.domain.company import CompanyId +from conode.domain.group import GroupId +from conode.domain.node import NodeAssociationId, NodeId +from conode.domain.node.model import ( MAX_ALLOWED_NODE_DESCRIPTION_LENGTH, MAX_ALLOWED_NODE_NAME_LENGTH, MIN_ALLOWED_NODE_NAME_LENGTH, diff --git a/src/prodik/presentation/schemas/offer.py b/src/conode/presentation/schemas/offer.py similarity index 73% rename from src/prodik/presentation/schemas/offer.py rename to src/conode/presentation/schemas/offer.py index ccd1271..8c13c39 100644 --- a/src/prodik/presentation/schemas/offer.py +++ b/src/conode/presentation/schemas/offer.py @@ -2,11 +2,11 @@ from pydantic import BaseModel -from prodik.domain.company import CompanyId -from prodik.domain.context import ContextId -from prodik.domain.group import GroupId -from prodik.domain.offer import OfferId, OfferStatus -from prodik.domain.role import PermissionType +from conode.domain.company import CompanyId +from conode.domain.context import ContextId +from conode.domain.group import GroupId +from conode.domain.offer import OfferId, OfferStatus +from conode.domain.role import PermissionType class SendOfferToCompanyRequest(BaseModel): diff --git a/src/prodik/presentation/schemas/role.py b/src/conode/presentation/schemas/role.py similarity index 91% rename from src/prodik/presentation/schemas/role.py rename to src/conode/presentation/schemas/role.py index 2404c04..cdd57a6 100644 --- a/src/prodik/presentation/schemas/role.py +++ b/src/conode/presentation/schemas/role.py @@ -2,15 +2,15 @@ from pydantic import BaseModel, Field -from prodik.domain.company import CompanyId -from prodik.domain.role import ( +from conode.domain.company import CompanyId +from conode.domain.role import ( EntityType, PermissionType, RoleId, RolePermissionEntityId, RolePermissionId, ) -from prodik.domain.role.model import ( +from conode.domain.role.model import ( MAX_ALLOWED_ROLE_NAME_LENGTH, MIN_ALLOWED_ROLE_NAME_LENGTH, ) diff --git a/src/prodik/presentation/schemas/user.py b/src/conode/presentation/schemas/user.py similarity index 80% rename from src/prodik/presentation/schemas/user.py rename to src/conode/presentation/schemas/user.py index f55a2cc..4590124 100644 --- a/src/prodik/presentation/schemas/user.py +++ b/src/conode/presentation/schemas/user.py @@ -2,9 +2,9 @@ from pydantic import BaseModel, EmailStr, Field -from prodik.domain.role import RoleId -from prodik.domain.user import UserId -from prodik.domain.user.model import ( +from conode.domain.role import RoleId +from conode.domain.user import UserId +from conode.domain.user.model import ( MAX_ALLOWED_BIO_LENGTH, MAX_ALLOWED_FIRST_NAME_LENGTH, MAX_ALLOWED_LAST_NAME_LENGTH, @@ -38,15 +38,9 @@ class UpdateCurrentUserProfileRequest(BaseModel): ), ] password: Annotated[str, Field(min_length=7, max_length=100)] - email: EmailStr bio: Annotated[str, Field(max_length=MAX_ALLOWED_BIO_LENGTH)] -class UpdateCurrentUserPasswordRequest(BaseModel): - old_password: Annotated[str, Field(min_length=7, max_length=100)] - new_password: Annotated[str, Field(min_length=7, max_length=100)] - - class UserSchema(BaseModel): id: UserId username: str diff --git a/src/prodik/presentation/views/__init__.py b/src/conode/presentation/views/__init__.py similarity index 90% rename from src/prodik/presentation/views/__init__.py rename to src/conode/presentation/views/__init__.py index a1e07ef..d76ac65 100644 --- a/src/prodik/presentation/views/__init__.py +++ b/src/conode/presentation/views/__init__.py @@ -1,4 +1,3 @@ -from .auth import router as auth from .company import router as company from .context import router as context from .edge import router as edge @@ -10,7 +9,6 @@ from .user import router as user __all__ = ( - "auth", "company", "context", "edge", diff --git a/src/prodik/presentation/views/company.py b/src/conode/presentation/views/company.py similarity index 83% rename from src/prodik/presentation/views/company.py rename to src/conode/presentation/views/company.py index 861fb33..d2194b0 100644 --- a/src/prodik/presentation/views/company.py +++ b/src/conode/presentation/views/company.py @@ -3,13 +3,13 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.register_company import ( +from conode.application.register_company import ( RegisterCompanyInteractor, RegisterCompanyRequestDTO, ) -from prodik.application.verify_company import VerifyCompanyInteractor -from prodik.domain.company import CompanyId -from prodik.presentation.schemas.company import CompanySchema, RegisterCompanyRequest +from conode.application.verify_company import VerifyCompanyInteractor +from conode.domain.company import CompanyId +from conode.presentation.schemas.company import CompanySchema, RegisterCompanyRequest router = APIRouter(tags=["companies"], prefix="/companies", route_class=DishkaRoute) diff --git a/src/prodik/presentation/views/context.py b/src/conode/presentation/views/context.py similarity index 87% rename from src/prodik/presentation/views/context.py rename to src/conode/presentation/views/context.py index fb81aa1..105afe1 100644 --- a/src/prodik/presentation/views/context.py +++ b/src/conode/presentation/views/context.py @@ -3,13 +3,13 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.manage_context import ( +from conode.application.manage_context import ( CreateContextInteractor, CreateContextRequestDTO, DeleteContextInteractor, ) -from prodik.domain.context import ContextId -from prodik.presentation.schemas.context import ContextSchema, CreateContextRequest +from conode.domain.context import ContextId +from conode.presentation.schemas.context import ContextSchema, CreateContextRequest router = APIRouter(tags=["contexts"], prefix="/contexts", route_class=DishkaRoute) diff --git a/src/prodik/presentation/views/edge.py b/src/conode/presentation/views/edge.py similarity index 89% rename from src/prodik/presentation/views/edge.py rename to src/conode/presentation/views/edge.py index 3cdaf89..fdef34a 100644 --- a/src/prodik/presentation/views/edge.py +++ b/src/conode/presentation/views/edge.py @@ -3,24 +3,24 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.manage_edge import ( +from conode.application.manage_edge import ( CreateEdgeInteractor, CreateEdgeRequestDTO, DeleteEdgeInteractor, ) -from prodik.application.receive_graph_statistics import ( +from conode.application.receive_graph_statistics import ( FindShortestPathInteractor, FindShortestPathRequestDTO, ) -from prodik.application.update_edge_weight import ( +from conode.application.update_edge_weight import ( DecrementEdgeWeightInteractor, IncrementEdgeWeightInteractor, UpdateEdgeWeightInteractor, ) -from prodik.domain.context import ContextId -from prodik.domain.edge import EdgeId -from prodik.domain.node import NodeId -from prodik.presentation.schemas.edge import ( +from conode.domain.context import ContextId +from conode.domain.edge import EdgeId +from conode.domain.node import NodeId +from conode.presentation.schemas.edge import ( CreateEdgeRequest, EdgeSchema, UpdateEdgeWeightRequest, diff --git a/src/prodik/presentation/views/group.py b/src/conode/presentation/views/group.py similarity index 88% rename from src/prodik/presentation/views/group.py rename to src/conode/presentation/views/group.py index 4ddd75d..2cdea35 100644 --- a/src/prodik/presentation/views/group.py +++ b/src/conode/presentation/views/group.py @@ -3,13 +3,13 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.manage_group import ( +from conode.application.manage_group import ( CreateGroupInteractor, CreateGroupRequestDTO, DeleteGroupInteractor, ) -from prodik.domain.group import GroupId -from prodik.presentation.schemas.group import CreateGroupRequest, GroupSchema +from conode.domain.group import GroupId +from conode.presentation.schemas.group import CreateGroupRequest, GroupSchema router = APIRouter(tags=["groups"], prefix="/groups", route_class=DishkaRoute) diff --git a/src/prodik/presentation/views/node.py b/src/conode/presentation/views/node.py similarity index 87% rename from src/prodik/presentation/views/node.py rename to src/conode/presentation/views/node.py index bbeef6b..ad13dde 100644 --- a/src/prodik/presentation/views/node.py +++ b/src/conode/presentation/views/node.py @@ -3,30 +3,30 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.attach_node_to_group import ( +from conode.application.attach_node_to_group import ( AttachNodeInteractor, AttachNodeRequestDTO, ) -from prodik.application.detach_node_from_group import DetachNodeInteractor -from prodik.application.manage_node import ( +from conode.application.detach_node_from_group import DetachNodeInteractor +from conode.application.manage_node import ( CreateNodeInteractor, CreateNodeRequestDTO, DeleteNodeInteractor, ) -from prodik.application.receive_graph_statistics import ( +from conode.application.receive_graph_statistics import ( GetNodeNeighboursInteractor, GetNodeNeighboursRequestDTO, ) -from prodik.application.receive_node_info import GetNodesByGroupInteractor -from prodik.application.update_node import ( +from conode.application.receive_node_info import GetNodesByGroupInteractor +from conode.application.update_node import ( UpdateNodeInteractor, UpdateNodeRequestDTO, ) -from prodik.domain.context import ContextId -from prodik.domain.group import GroupId -from prodik.domain.node import NodeAssociationId, NodeId -from prodik.presentation.schemas.edge import EdgeSchema -from prodik.presentation.schemas.node import ( +from conode.domain.context import ContextId +from conode.domain.group import GroupId +from conode.domain.node import NodeAssociationId, NodeId +from conode.presentation.schemas.edge import EdgeSchema +from conode.presentation.schemas.node import ( AttachNodeRequest, CreateNodeRequest, NodeAssociationSchema, diff --git a/src/prodik/presentation/views/offer.py b/src/conode/presentation/views/offer.py similarity index 92% rename from src/prodik/presentation/views/offer.py rename to src/conode/presentation/views/offer.py index 75a6fef..b4b84c2 100644 --- a/src/prodik/presentation/views/offer.py +++ b/src/conode/presentation/views/offer.py @@ -3,14 +3,14 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.share_graph import ( +from conode.application.share_graph import ( AcceptOfferInteractor, DeclineOfferInteractor, SendOfferToCompanyInteractor, SendOfferToCompanyRequestDTO, ) -from prodik.domain.offer import OfferId -from prodik.presentation.schemas.offer import OfferSchema, SendOfferToCompanyRequest +from conode.domain.offer import OfferId +from conode.presentation.schemas.offer import OfferSchema, SendOfferToCompanyRequest router = APIRouter(tags=["offers"], prefix="/offers", route_class=DishkaRoute) diff --git a/src/prodik/presentation/views/role.py b/src/conode/presentation/views/role.py similarity index 95% rename from src/prodik/presentation/views/role.py rename to src/conode/presentation/views/role.py index 557568b..f329eb8 100644 --- a/src/prodik/presentation/views/role.py +++ b/src/conode/presentation/views/role.py @@ -3,7 +3,7 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.manage_role import ( +from conode.application.manage_role import ( CreatePermissionRequestDTO, CreateRoleInteractor, CreateRoleRequestDTO, @@ -12,8 +12,8 @@ UpdateRoleInteractor, UpdateRoleRequestDTO, ) -from prodik.domain.role import RoleId -from prodik.presentation.schemas.role import ( +from conode.domain.role import RoleId +from conode.presentation.schemas.role import ( CreateRoleRequest, PermissionSchema, RoleSchema, diff --git a/src/prodik/presentation/views/root.py b/src/conode/presentation/views/root.py similarity index 100% rename from src/prodik/presentation/views/root.py rename to src/conode/presentation/views/root.py diff --git a/src/prodik/presentation/views/user.py b/src/conode/presentation/views/user.py similarity index 67% rename from src/prodik/presentation/views/user.py rename to src/conode/presentation/views/user.py index 0d7dc95..4ea6216 100644 --- a/src/prodik/presentation/views/user.py +++ b/src/conode/presentation/views/user.py @@ -3,27 +3,21 @@ from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter -from prodik.application.manage_credentials import ( - UpdateCurrentUserPasswordInteractor, - UpdateCurrentUserPasswordRequestDTO, -) -from prodik.application.manage_profile import ( +from conode.application.manage_profile import ( UpdateCurrentUserProfileInteractor, UpdateCurrentUserProfileRequestDTO, ) -from prodik.application.manage_user_rights import ( +from conode.application.manage_user_rights import ( GiveRoleToUserInteractor, RevokeRoleFromUserInteractor, ) -from prodik.application.receive_user_info import ( +from conode.application.receive_user_info import ( GetCurrentUserInteractor, GetUserByUsernameInteractor, ) -from prodik.domain.role import RoleId -from prodik.domain.user import UserId -from prodik.presentation.schemas.auth import AuthResponse -from prodik.presentation.schemas.user import ( - UpdateCurrentUserPasswordRequest, +from conode.domain.role import RoleId +from conode.domain.user import UserId +from conode.presentation.schemas.user import ( UpdateCurrentUserProfileRequest, UserSchema, ) @@ -41,7 +35,6 @@ async def update_current_user_profile( username=request.username, first_name=request.first_name, last_name=request.last_name, - email=request.email, bio=request.bio, ), ) @@ -62,24 +55,6 @@ async def get_current_user_profile( ) -@router.put("/me/password") -async def update_current_user_password( - request: UpdateCurrentUserPasswordRequest, - interactor: FromDishka[UpdateCurrentUserPasswordInteractor], -) -> AuthResponse: - result = await interactor.execute( - UpdateCurrentUserPasswordRequestDTO( - old_password=request.old_password, - new_password=request.new_password, - ), - ) - return AuthResponse( - access_token=result.access_token, - refresh_token=result.access_token, - expires_in=result.expires_in, - ) - - @router.get("/{username}") async def get_user_by_username( username: str, diff --git a/src/prodik/py.typed b/src/conode/py.typed similarity index 100% rename from src/prodik/py.typed rename to src/conode/py.typed diff --git a/src/prodik/application/authorization/__init__.py b/src/prodik/application/authorization/__init__.py deleted file mode 100644 index b962f61..0000000 --- a/src/prodik/application/authorization/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .login import LoginInteractor, LoginRequestDTO -from .refresh import RefreshTokenInteractor -from .register import RegisterInteractor, RegisterRequestDTO - -__all__ = ( - "LoginInteractor", - "LoginRequestDTO", - "RefreshTokenInteractor", - "RegisterInteractor", - "RegisterRequestDTO", -) diff --git a/src/prodik/application/authorization/login.py b/src/prodik/application/authorization/login.py deleted file mode 100644 index c08ed5e..0000000 --- a/src/prodik/application/authorization/login.py +++ /dev/null @@ -1,102 +0,0 @@ -from dataclasses import dataclass -from uuid import uuid4 - -from prodik.application.errors import ( - AuthorizationNotFoundError, - InvalidCredentialsError, - UserNotFoundError, -) -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.password_hasher import PasswordHasher -from prodik.application.interfaces.repositories import ( - LocalAuthorizationRepository, - SessionRepository, - UserRepository, -) -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, -) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.authorization import Session, SessionId -from prodik.domain.user import Email - - -@dataclass(frozen=True, slots=True, kw_only=True) -class LoginRequestDTO: - email: str - password: str - - -@dataclass(frozen=True, slots=True, kw_only=True) -class LoginResponseDTO: - access_token: str - refresh_token: str - expires_in: int - - -@dataclass -class LoginInteractor: - user_repository: UserRepository - session_repository: SessionRepository - authorization_repository: LocalAuthorizationRepository - password_hasher: PasswordHasher - identity_provider: IdentityProvider - transaction_manager: TransactionManager - access_token_manager: AccessTokenManager - refresh_token_manager: RefreshTokenManager - - async def execute(self, request: LoginRequestDTO) -> LoginResponseDTO: - async with self.transaction_manager: - host = self.identity_provider.get_current_ip() - - user = await self.user_repository.get_by_email(Email(request.email)) - if user is None: - raise UserNotFoundError( - "User not found", - [{"key": "email", "value": request.email}], - ) - - authorization = await self.authorization_repository.get_by_user_id(user.id) - if authorization is None: - raise AuthorizationNotFoundError( - "Authorization not found", - [{"key": "user_id", "value": user.id}], - ) - - if not self.password_hasher.verify( - authorization.password, - request.password, - ): - raise InvalidCredentialsError( - "Invalid email or password", - [ - {"key": "email", "value": request.email}, - {"key": "password", "value": request.password}, - ], - ) - - access_token, expires_in = self.access_token_manager.encode(user) - refresh_token = self.refresh_token_manager.encode() - - session = await self.session_repository.get_by_host_and_user_id( - user.id, host - ) - if session is None: - session = Session.new( - session_id=SessionId(uuid4()), - user=user, - host=host, - token=refresh_token, - ) - await self.session_repository.create(session) - else: - prev_token = session.token - session.update_token(refresh_token) - await self.session_repository.update(prev_token, session) - - return LoginResponseDTO( - access_token=access_token, - refresh_token=refresh_token, - expires_in=expires_in, - ) diff --git a/src/prodik/application/authorization/refresh.py b/src/prodik/application/authorization/refresh.py deleted file mode 100644 index 9ccc4b5..0000000 --- a/src/prodik/application/authorization/refresh.py +++ /dev/null @@ -1,47 +0,0 @@ -from dataclasses import dataclass - -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.repositories import SessionRepository, UserRepository -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, -) -from prodik.application.interfaces.transaction_manager import TransactionManager - - -@dataclass(slots=True, frozen=True, kw_only=True) -class RefreshTokenResponse: - access_token: str - refresh_token: str - expires_in: int - - -@dataclass -class RefreshTokenInteractor: - user_repository: UserRepository - identity_provider: IdentityProvider - session_repository: SessionRepository - access_token_manager: AccessTokenManager - refresh_token_manager: RefreshTokenManager - transaction_manager: TransactionManager - - async def execute(self, token: str) -> RefreshTokenResponse: - async with self.transaction_manager: - user_meta = self.identity_provider.get_current_user_meta() - - user = await self.user_repository.get_by_id(user_meta["user_id"]) - session = await self.session_repository.get_by_token(token) - - access_token, expires_in = self.access_token_manager.encode(user) - refresh_token = self.refresh_token_manager.encode() - - prev_token = session.token - session.update_token(refresh_token) - - await self.session_repository.update(prev_token, session) - - return RefreshTokenResponse( - access_token=access_token, - refresh_token=refresh_token, - expires_in=expires_in, - ) diff --git a/src/prodik/application/authorization/register.py b/src/prodik/application/authorization/register.py deleted file mode 100644 index 7ed66bd..0000000 --- a/src/prodik/application/authorization/register.py +++ /dev/null @@ -1,105 +0,0 @@ -from dataclasses import dataclass -from uuid import uuid4 - -from prodik.application.errors import UserAlreadyExistsError -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.password_hasher import PasswordHasher -from prodik.application.interfaces.repositories import ( - LocalAuthorizationRepository, - SessionRepository, - UserRepository, -) -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, -) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.authorization import ( - LocalAuthorization, - LocalAuthorizationId, - Session, - SessionId, -) -from prodik.domain.user import Email, User, UserId, Username - - -@dataclass(frozen=True, slots=True, kw_only=True) -class RegisterRequestDTO: - first_name: str - last_name: str - username: str - password: str - email: str - - -@dataclass(frozen=True, slots=True, kw_only=True) -class RegisterResponseDTO: - access_token: str - refresh_token: str - expires_in: int - - -@dataclass -class RegisterInteractor: - user_repository: UserRepository - session_repository: SessionRepository - authorization_repository: LocalAuthorizationRepository - access_token_manager: AccessTokenManager - refresh_token_manager: RefreshTokenManager - transaction_manager: TransactionManager - identity_provider: IdentityProvider - password_hasher: PasswordHasher - - async def execute(self, request: RegisterRequestDTO) -> RegisterResponseDTO: - async with self.transaction_manager: - host = self.identity_provider.get_current_ip() - user = await self.user_repository.get_by_username_or_email( - username=Username(request.username), - email=Email(request.email), - ) - - if user is not None: - raise UserAlreadyExistsError( - "User with this username or email already exists", - [ - {"key": "email", "value": request.email}, - {"key": "username", "value": request.username}, - ], - ) - - user = User.new( - user_id=UserId(uuid4()), - first_name=request.first_name, - last_name=request.last_name, - username=request.username, - email=request.email, - bio="", - ) - - access_token, expires_in = self.access_token_manager.encode(user) - refresh_token = self.refresh_token_manager.encode() - - session = Session.new( - session_id=SessionId(uuid4()), - user=user, - host=host, - token=refresh_token, - ) - - hashed_password = self.password_hasher.hash(request.password) - - authorization = LocalAuthorization.new( - local_authorization_id=LocalAuthorizationId(uuid4()), - user=user, - password=hashed_password, - ) - - await self.user_repository.create(user=user) - await self.session_repository.create(session=session) - await self.authorization_repository.create(authorization=authorization) - - return RegisterResponseDTO( - access_token=access_token, - refresh_token=refresh_token, - expires_in=expires_in, - ) diff --git a/src/prodik/application/interfaces/repositories/authorization/__init__.py b/src/prodik/application/interfaces/repositories/authorization/__init__.py deleted file mode 100644 index 019918b..0000000 --- a/src/prodik/application/interfaces/repositories/authorization/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .local_authorization import LocalAuthorizationRepository -from .session import SessionRepository - -__all__ = ( - "LocalAuthorizationRepository", - "SessionRepository", -) diff --git a/src/prodik/application/interfaces/repositories/authorization/local_authorization.py b/src/prodik/application/interfaces/repositories/authorization/local_authorization.py deleted file mode 100644 index af786f3..0000000 --- a/src/prodik/application/interfaces/repositories/authorization/local_authorization.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Protocol - -from prodik.domain.authorization import LocalAuthorization -from prodik.domain.user import UserId - - -class LocalAuthorizationRepository(Protocol): - async def create(self, authorization: LocalAuthorization) -> None: ... - async def update(self, authorization: LocalAuthorization) -> None: ... - async def get_by_user_id(self, user_id: UserId) -> LocalAuthorization | None: ... - async def get_all_by_user_id(self, user_id: UserId) -> list[LocalAuthorization]: ... diff --git a/src/prodik/application/interfaces/repositories/authorization/session.py b/src/prodik/application/interfaces/repositories/authorization/session.py deleted file mode 100644 index 646ae3c..0000000 --- a/src/prodik/application/interfaces/repositories/authorization/session.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Protocol - -from prodik.domain.authorization import Session -from prodik.domain.user import UserId - - -class SessionRepository(Protocol): - async def create(self, session: Session) -> None: ... - async def update(self, prev_token: str, session: Session) -> None: ... - async def get_by_host_and_user_id( - self, user_id: UserId, host: str - ) -> Session | None: ... - async def get_by_token(self, token: str) -> Session: ... diff --git a/src/prodik/application/interfaces/token_managers/__init__.py b/src/prodik/application/interfaces/token_managers/__init__.py deleted file mode 100644 index 45d99f2..0000000 --- a/src/prodik/application/interfaces/token_managers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .access_token_manager import ( - AccessTokenManager, - AccessTokenManagerResponse, - UserMeta, -) -from .refresh_token_manager import RefreshTokenManager - -__all__ = ( - "AccessTokenManager", - "AccessTokenManagerResponse", - "RefreshTokenManager", - "UserMeta", -) diff --git a/src/prodik/application/interfaces/token_managers/access_token_manager.py b/src/prodik/application/interfaces/token_managers/access_token_manager.py deleted file mode 100644 index 4ecab4e..0000000 --- a/src/prodik/application/interfaces/token_managers/access_token_manager.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import NamedTuple, Protocol, TypedDict - -from prodik.domain.user import User, UserId, UserSystemRole - - -class UserMeta(TypedDict): - system_role: UserSystemRole - user_id: UserId - revision: int - - -class AccessTokenManagerResponse(NamedTuple): - token: str - expires_in: int - - -class AccessTokenManager(Protocol): - def encode(self, user: User) -> AccessTokenManagerResponse: ... - def decode(self, token: str) -> UserMeta: ... diff --git a/src/prodik/application/interfaces/token_managers/refresh_token_manager.py b/src/prodik/application/interfaces/token_managers/refresh_token_manager.py deleted file mode 100644 index 78ddc8d..0000000 --- a/src/prodik/application/interfaces/token_managers/refresh_token_manager.py +++ /dev/null @@ -1,5 +0,0 @@ -from typing import Protocol - - -class RefreshTokenManager(Protocol): - def encode(self) -> str: ... diff --git a/src/prodik/application/manage_credentials/__init__.py b/src/prodik/application/manage_credentials/__init__.py deleted file mode 100644 index c48a973..0000000 --- a/src/prodik/application/manage_credentials/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .update_current_user_password import ( - UpdateCurrentUserPasswordInteractor, - UpdateCurrentUserPasswordRequestDTO, -) - -__all__ = ("UpdateCurrentUserPasswordInteractor", "UpdateCurrentUserPasswordRequestDTO") diff --git a/src/prodik/application/manage_credentials/update_current_user_password.py b/src/prodik/application/manage_credentials/update_current_user_password.py deleted file mode 100644 index 17930ba..0000000 --- a/src/prodik/application/manage_credentials/update_current_user_password.py +++ /dev/null @@ -1,103 +0,0 @@ -from dataclasses import dataclass - -from prodik.application.errors import ( - AuthorizationNotFoundError, - InvalidOldPasswordError, - SessionNotFoundError, -) -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.password_hasher import PasswordHasher -from prodik.application.interfaces.repositories import ( - LocalAuthorizationRepository, - SessionRepository, - UserRepository, -) -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, -) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import AccessControlService - - -@dataclass(frozen=True, slots=True, kw_only=True) -class UpdateCurrentUserPasswordRequestDTO: - old_password: str - new_password: str - - -@dataclass(frozen=True, slots=True, kw_only=True) -class UpdateCurrentUserPasswordResponseDTO: - access_token: str - refresh_token: str - expires_in: int - - -@dataclass -class UpdateCurrentUserPasswordInteractor: - user_repository: UserRepository - session_repository: SessionRepository - local_authorization_repository: LocalAuthorizationRepository - transaction_manager: TransactionManager - identity_provider: IdentityProvider - password_hasher: PasswordHasher - access_token_manager: AccessTokenManager - refresh_token_manager: RefreshTokenManager - access_control_service: AccessControlService - - async def execute( - self, - request: UpdateCurrentUserPasswordRequestDTO, - ) -> UpdateCurrentUserPasswordResponseDTO: - async with self.transaction_manager: - host = self.identity_provider.get_current_ip() - user = await self.access_control_service.get_authorized_user() - - authorization = await self.local_authorization_repository.get_by_user_id( - user.id, - ) - if authorization is None: - raise AuthorizationNotFoundError( - "Authorization by user id not found", - [{"key": "user_id", "value": user.id}], - ) - - if not self.password_hasher.verify( - authorization.password, - request.old_password, - ): - raise InvalidOldPasswordError( - "Invalid old password", - [ - {"key": "old_password", "value": request.old_password}, - ], - ) - - hashed_password = self.password_hasher.hash(request.new_password) - authorization.update_password(hashed_password) - user.increment_revision() - - session = await self.session_repository.get_by_host_and_user_id( - user.id, host - ) - if session is None: - raise SessionNotFoundError( - "Session not found", - [{"key": "host", "value": host}], - ) - - access_token, expires_in = self.access_token_manager.encode(user) - refresh_token = self.refresh_token_manager.encode() - - prev_token = session.token - session.update_token(refresh_token) - - await self.user_repository.update(user) - await self.local_authorization_repository.update(authorization) - await self.session_repository.update(prev_token, session) - - return UpdateCurrentUserPasswordResponseDTO( - access_token=access_token, - refresh_token=refresh_token, - expires_in=expires_in, - ) diff --git a/src/prodik/bootstrap/di/providers/cache.py b/src/prodik/bootstrap/di/providers/cache.py deleted file mode 100644 index 318630a..0000000 --- a/src/prodik/bootstrap/di/providers/cache.py +++ /dev/null @@ -1,20 +0,0 @@ -from collections.abc import AsyncIterator - -from dishka import Provider, Scope, provide -from redis.asyncio import Redis - -from prodik.infrastructure.config import CacheConfig - - -class CacheConnectionProvider(Provider): - @provide(scope=Scope.APP) - async def get_redis_connection(self, config: CacheConfig) -> AsyncIterator[Redis]: - client = Redis( - host=config.host, - port=config.port, - decode_responses=True, - ) - - yield client - - await client.aclose() diff --git a/src/prodik/domain/authorization/__init__.py b/src/prodik/domain/authorization/__init__.py deleted file mode 100644 index 696af78..0000000 --- a/src/prodik/domain/authorization/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from .local import LocalAuthorization, LocalAuthorizationId -from .oauth import OAuthAuthorization, OAuthAuthorizationId -from .session import Session, SessionId - -__all__ = ( - "LocalAuthorization", - "LocalAuthorizationId", - "OAuthAuthorization", - "OAuthAuthorizationId", - "Session", - "SessionId", -) diff --git a/src/prodik/domain/authorization/local/__init__.py b/src/prodik/domain/authorization/local/__init__.py deleted file mode 100644 index b45efae..0000000 --- a/src/prodik/domain/authorization/local/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .model import LocalAuthorization, LocalAuthorizationId - -__all__ = ( - "LocalAuthorization", - "LocalAuthorizationId", -) diff --git a/src/prodik/domain/authorization/local/model.py b/src/prodik/domain/authorization/local/model.py deleted file mode 100644 index 00f05ae..0000000 --- a/src/prodik/domain/authorization/local/model.py +++ /dev/null @@ -1,34 +0,0 @@ -from dataclasses import dataclass -from datetime import UTC, datetime -from typing import NewType, Self -from uuid import UUID - -from prodik.domain.shared import Entity -from prodik.domain.user import User, UserId - -LocalAuthorizationId = NewType("LocalAuthorizationId", UUID) - - -@dataclass -class LocalAuthorization(Entity[LocalAuthorizationId]): - user_id: UserId - password: str - - @classmethod - def new( - cls, - local_authorization_id: LocalAuthorizationId, - user: User, - password: str, - ) -> Self: - now = datetime.now(UTC) - return cls( - id=local_authorization_id, - user_id=user.id, - password=password, - created_at=now, - updated_at=now, - ) - - def update_password(self, password: str) -> None: - self.password = password diff --git a/src/prodik/domain/authorization/oauth/__init__.py b/src/prodik/domain/authorization/oauth/__init__.py deleted file mode 100644 index 27465e7..0000000 --- a/src/prodik/domain/authorization/oauth/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .model import OAuthAuthorization, OAuthAuthorizationId - -__all__ = ( - "OAuthAuthorization", - "OAuthAuthorizationId", -) diff --git a/src/prodik/domain/authorization/oauth/model.py b/src/prodik/domain/authorization/oauth/model.py deleted file mode 100644 index 30815a4..0000000 --- a/src/prodik/domain/authorization/oauth/model.py +++ /dev/null @@ -1,24 +0,0 @@ -from dataclasses import dataclass -from datetime import UTC, datetime -from typing import NewType, Self -from uuid import UUID - -from prodik.domain.shared import Entity -from prodik.domain.user import User, UserId - -OAuthAuthorizationId = NewType("OAuthAuthorizationId", UUID) - - -@dataclass -class OAuthAuthorization(Entity[OAuthAuthorizationId]): - user_id: UserId - - @classmethod - def new(cls, oauth_authorization_id: OAuthAuthorizationId, user: User) -> Self: - now = datetime.now(UTC) - return cls( - id=oauth_authorization_id, - user_id=user.id, - created_at=now, - updated_at=now, - ) diff --git a/src/prodik/domain/authorization/session/__init__.py b/src/prodik/domain/authorization/session/__init__.py deleted file mode 100644 index 3b8f490..0000000 --- a/src/prodik/domain/authorization/session/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .model import Session, SessionId - -__all__ = ( - "Session", - "SessionId", -) diff --git a/src/prodik/domain/authorization/session/model.py b/src/prodik/domain/authorization/session/model.py deleted file mode 100644 index 3994814..0000000 --- a/src/prodik/domain/authorization/session/model.py +++ /dev/null @@ -1,32 +0,0 @@ -from dataclasses import dataclass -from datetime import UTC, datetime -from typing import NewType, Self -from uuid import UUID - -from prodik.domain.shared import Entity -from prodik.domain.user import User, UserId - -SessionId = NewType("SessionId", UUID) - - -@dataclass -class Session(Entity[SessionId]): - user_id: UserId - token: str - host: str - - @classmethod - def new(cls, session_id: SessionId, user: User, host: str, token: str) -> Self: - now = datetime.now(UTC) - return cls( - id=session_id, - host=host, - token=token, - user_id=user.id, - created_at=now, - updated_at=now, - ) - - def update_token(self, token: str) -> None: - self.token = token - self.touch() diff --git a/src/prodik/infrastructure/identity_provider.py b/src/prodik/infrastructure/identity_provider.py deleted file mode 100644 index 1acd7a6..0000000 --- a/src/prodik/infrastructure/identity_provider.py +++ /dev/null @@ -1,60 +0,0 @@ -from dataclasses import dataclass -from typing import Final - -import structlog -from fastapi import Request -from jwt import PyJWTError - -from prodik.application.errors import FailedToReadClientError, InvalidTokenError -from prodik.application.interfaces.identity_provider import IdentityProvider -from prodik.application.interfaces.token_managers import AccessTokenManager, UserMeta - -TOKEN_TYPE: Final = "Bearer" # noqa: S105 -TOKEN_SECTIONS: Final = 2 - -logger = structlog.get_logger() - - -@dataclass -class IdentityProviderImpl(IdentityProvider): - request: Request - access_token_manager: AccessTokenManager - - def get_current_ip(self) -> str: - if self.request.client is None: - raise FailedToReadClientError("Failed to read client error", None) - - return self.request.client.host - - def get_current_user_meta(self) -> UserMeta: - header = self.request.headers.get("Authorization") - if header is None: - raise InvalidTokenError("Authorization token not found", None) - - header_content = header.split(" ") - if len(header_content) != TOKEN_SECTIONS: - raise InvalidTokenError( - "Invalid authorization token format", - [{"key": "Authorization", "value": header}], - ) - - token_type, token = header_content - if token_type != TOKEN_TYPE: - raise InvalidTokenError( - "Invalid authorization token type", - [{"key": "Authorization", "value": token_type}], - ) - - try: - content = self.access_token_manager.decode(token) - except PyJWTError as e: - raise InvalidTokenError( - "Invalid authorization token", - [{"key": "Authorization", "value": token}], - ) from e - - return UserMeta( - system_role=content["system_role"], - user_id=content["user_id"], - revision=content["revision"], - ) diff --git a/src/prodik/infrastructure/repositories/authorization/__init__.py b/src/prodik/infrastructure/repositories/authorization/__init__.py deleted file mode 100644 index 8e0f909..0000000 --- a/src/prodik/infrastructure/repositories/authorization/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .local_authorization import LocalAuthorizationRepositoryImpl -from .session import SessionRepositoryImpl - -__all__ = ( - "LocalAuthorizationRepositoryImpl", - "SessionRepositoryImpl", -) diff --git a/src/prodik/infrastructure/repositories/authorization/local_authorization.py b/src/prodik/infrastructure/repositories/authorization/local_authorization.py deleted file mode 100644 index b5c5095..0000000 --- a/src/prodik/infrastructure/repositories/authorization/local_authorization.py +++ /dev/null @@ -1,76 +0,0 @@ -from dataclasses import dataclass - -import structlog -from sqlalchemy import insert, select, update -from sqlalchemy.ext.asyncio import AsyncSession - -from prodik.application.interfaces.repositories import LocalAuthorizationRepository -from prodik.domain.authorization import LocalAuthorization -from prodik.domain.user import UserId - -logger = structlog.get_logger() - - -@dataclass -class LocalAuthorizationRepositoryImpl(LocalAuthorizationRepository): - session: AsyncSession - - async def create(self, authorization: LocalAuthorization) -> None: - logger.debug( - "Repository create local authorization", - authorization_id=authorization.id, - ) - await self.session.execute( - insert(LocalAuthorization).values( - id=authorization.id, - user_id=authorization.user_id, - password=authorization.password, - created_at=authorization.created_at, - updated_at=authorization.updated_at, - ), - ) - - async def update(self, authorization: LocalAuthorization) -> None: - logger.debug( - "Repository update local authorization", - authorization_id=authorization.id, - ) - await self.session.execute( - update(LocalAuthorization) - .where( - LocalAuthorization.id == authorization.id, # type: ignore - ) - .values( - user_id=authorization.user_id, - password=authorization.password, - ), - ) - - async def get_by_user_id(self, user_id: UserId) -> LocalAuthorization | None: - logger.debug("Repository get local authorization by user id", user_id=user_id) - result = await self.session.execute( - select(LocalAuthorization).where(LocalAuthorization.user_id == user_id), # type: ignore - ) - - authorization = result.scalar_one_or_none() - logger.debug( - "Repository fetched local authorization by user id", - found=authorization is not None, - ) - return authorization - - async def get_all_by_user_id(self, user_id: UserId) -> list[LocalAuthorization]: - logger.debug("Repository get local authorizations by user id", user_id=user_id) - result = await self.session.execute( - select(LocalAuthorization).where( - LocalAuthorization.user_id == user_id # type: ignore - ) - ) - - result_local_authorizations = list(result.scalars().all()) - - logger.debug( - "Repository fetched edges by context id", - found_count=len(result_local_authorizations), - ) - return result_local_authorizations diff --git a/src/prodik/infrastructure/repositories/authorization/session.py b/src/prodik/infrastructure/repositories/authorization/session.py deleted file mode 100644 index 6d177d8..0000000 --- a/src/prodik/infrastructure/repositories/authorization/session.py +++ /dev/null @@ -1,118 +0,0 @@ -from dataclasses import dataclass -from datetime import datetime - -import structlog -from redis.asyncio import Redis - -from prodik.application.errors import SessionNotFoundError -from prodik.application.interfaces.repositories import SessionRepository -from prodik.domain.authorization import Session, SessionId -from prodik.domain.user import UserId -from prodik.infrastructure.config import CacheConfig - -logger = structlog.get_logger() - - -@dataclass -class SessionRepositoryImpl(SessionRepository): - client: Redis - config: CacheConfig - - async def create(self, session: Session) -> None: - logger.debug("Repository create session", session_id=session.id) - session_key = f"session:{session.id}" - - host_key = f"host:{session.host}:{session.user_id}" - token_key = f"token:{session.token}" - - await self.client.hset( - session_key, - mapping={ # type: ignore - "id": str(session.id), - "user_id": str(session.user_id), - "token": session.token, - "host": session.host, - "created_at": session.created_at.isoformat(), - "updated_at": session.updated_at.isoformat(), - }, - ) - - await self.client.set(host_key, str(session.id)) - await self.client.set(token_key, str(session.id)) - - await self.client.expire(session_key, self.config.ttl) - - async def update(self, prev_token: str, session: Session) -> None: - logger.debug("Repository update session", session_id=session.id) - - session_key = f"session:{session.id}" - token_key = f"token:{prev_token}" - - await self.client.hset( - session_key, - mapping={ # type: ignore - "token": session.token, - "updated_at": session.updated_at.isoformat(), - }, - ) - - await self.client.delete(token_key) - await self.client.set(token_key, str(session.id)) - - async def get_by_token(self, token: str) -> Session: - logger.debug("Repository get session by token") - - session_id = await self.client.get(f"token:{token}") - if session_id is None: - raise SessionNotFoundError( - "Session not found", - [{"key": "refresh_token", "value": token}], - ) - - data = await self.client.hgetall(f"session:{session_id}") # type: ignore - if data is None: - raise SessionNotFoundError( - "Session not found", - [{"key": "refresh_token", "value": token}], - ) - - session = Session( - id=SessionId(data["id"]), - user_id=UserId(data["user_id"]), - token=data["token"], - host=data["host"], - created_at=datetime.fromisoformat(data["created_at"]), - updated_at=datetime.fromisoformat(data["updated_at"]), - ) - - logger.debug("Repository fetched session by token", found=session is not None) - return session - - async def get_by_host_and_user_id( - self, user_id: UserId, host: str - ) -> Session | None: - logger.debug("Repository get session by host", host=host) - - session_id = await self.client.get(f"host:{host}:{user_id}") - if session_id is None: - return None - - data = await self.client.hgetall(f"session:{session_id}") # type: ignore - if data is None: - raise SessionNotFoundError( - "Session not found", - [{"key": "refresh_token", "value": session_id}], - ) - - session = Session( - id=SessionId(data["id"]), - user_id=UserId(data["user_id"]), - token=data["token"], - host=data["host"], - created_at=datetime.fromisoformat(data["created_at"]), - updated_at=datetime.fromisoformat(data["updated_at"]), - ) - - logger.debug("Repository fetched session by host", found=session is not None) - - return session diff --git a/src/prodik/infrastructure/token_managers/__init__.py b/src/prodik/infrastructure/token_managers/__init__.py deleted file mode 100644 index 775a8dc..0000000 --- a/src/prodik/infrastructure/token_managers/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .access_token_manager import AccessTokenManagerImpl -from .refresh_token_manager import RefreshTokenManagerImpl - -__all__ = ( - "AccessTokenManagerImpl", - "RefreshTokenManagerImpl", -) diff --git a/src/prodik/infrastructure/token_managers/access_token_manager.py b/src/prodik/infrastructure/token_managers/access_token_manager.py deleted file mode 100644 index 0db6af3..0000000 --- a/src/prodik/infrastructure/token_managers/access_token_manager.py +++ /dev/null @@ -1,43 +0,0 @@ -from dataclasses import dataclass -from datetime import UTC, datetime, timedelta - -import jwt - -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - AccessTokenManagerResponse, - UserMeta, -) -from prodik.domain.user import User -from prodik.infrastructure.config import SecretsConfig - - -@dataclass -class AccessTokenManagerImpl(AccessTokenManager): - _config: SecretsConfig - - def encode(self, user: User) -> AccessTokenManagerResponse: - now = datetime.now(tz=UTC) - token = jwt.encode( - { - "sub": str(user.id), - "system_role": user.system_role, - "revision": user.token_revision, - "iat": now, - "exp": now + timedelta(seconds=self._config.expires_in), - }, - self._config.secret, - algorithm="HS256", - ) - return AccessTokenManagerResponse( - token=token, - expires_in=self._config.expires_in, - ) - - def decode(self, token: str) -> UserMeta: - data = jwt.decode(token, self._config.secret, algorithms=["HS256"]) - return UserMeta( - system_role=data["system_role"], - user_id=data["sub"], - revision=data["revision"], - ) diff --git a/src/prodik/infrastructure/token_managers/refresh_token_manager.py b/src/prodik/infrastructure/token_managers/refresh_token_manager.py deleted file mode 100644 index 6db2f96..0000000 --- a/src/prodik/infrastructure/token_managers/refresh_token_manager.py +++ /dev/null @@ -1,10 +0,0 @@ -import secrets -from dataclasses import dataclass - -from prodik.application.interfaces.token_managers import RefreshTokenManager - - -@dataclass -class RefreshTokenManagerImpl(RefreshTokenManager): - def encode(self) -> str: - return secrets.token_urlsafe(32) diff --git a/src/prodik/presentation/schemas/auth.py b/src/prodik/presentation/schemas/auth.py deleted file mode 100644 index 4ca992d..0000000 --- a/src/prodik/presentation/schemas/auth.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Annotated - -from pydantic import BaseModel, EmailStr, Field - -from prodik.domain.user.model import ( - MAX_ALLOWED_FIRST_NAME_LENGTH, - MAX_ALLOWED_LAST_NAME_LENGTH, - MAX_ALLOWED_USERNAME_LENGTH, - MIN_ALLOWED_FIRST_NAME_LENGTH, - MIN_ALLOWED_LAST_NAME_LENGTH, - MIN_ALLOWED_USERNAME_LENGTH, -) - - -class RegisterRequest(BaseModel): - username: Annotated[ - str, - Field( - min_length=MIN_ALLOWED_USERNAME_LENGTH, - max_length=MAX_ALLOWED_USERNAME_LENGTH, - ), - ] - first_name: Annotated[ - str, - Field( - min_length=MIN_ALLOWED_FIRST_NAME_LENGTH, - max_length=MAX_ALLOWED_FIRST_NAME_LENGTH, - ), - ] - last_name: Annotated[ - str, - Field( - min_length=MIN_ALLOWED_LAST_NAME_LENGTH, - max_length=MAX_ALLOWED_LAST_NAME_LENGTH, - ), - ] - password: Annotated[str, Field(min_length=7, max_length=100)] - email: EmailStr - - -class LoginRequest(BaseModel): - email: EmailStr - password: str - - -class AuthResponse(BaseModel): - access_token: str - refresh_token: str - expires_in: int - - -class RefreshTokenRequest(BaseModel): - refresh_token: str diff --git a/src/prodik/presentation/views/auth.py b/src/prodik/presentation/views/auth.py deleted file mode 100644 index 5b497fa..0000000 --- a/src/prodik/presentation/views/auth.py +++ /dev/null @@ -1,75 +0,0 @@ -from http import HTTPStatus - -from dishka.integrations.fastapi import DishkaRoute, FromDishka -from fastapi import APIRouter - -from prodik.application.authorization import ( - LoginInteractor, - LoginRequestDTO, - RefreshTokenInteractor, - RegisterInteractor, - RegisterRequestDTO, -) -from prodik.presentation.schemas.auth import ( - AuthResponse, - LoginRequest, - RefreshTokenRequest, - RegisterRequest, -) - -router = APIRouter(tags=["authorization"], route_class=DishkaRoute, prefix="/auth") - - -@router.post("/register", status_code=HTTPStatus.CREATED) -async def register( - request: RegisterRequest, - interactor: FromDishka[RegisterInteractor], -) -> AuthResponse: - result = await interactor.execute( - RegisterRequestDTO( - first_name=request.first_name, - last_name=request.last_name, - username=request.username, - password=request.password, - email=request.email, - ), - ) - - return AuthResponse( - access_token=result.access_token, - refresh_token=result.refresh_token, - expires_in=result.expires_in, - ) - - -@router.post("/login") -async def login( - request: LoginRequest, - interactor: FromDishka[LoginInteractor], -) -> AuthResponse: - result = await interactor.execute( - LoginRequestDTO( - password=request.password, - email=request.email, - ), - ) - - return AuthResponse( - access_token=result.access_token, - refresh_token=result.refresh_token, - expires_in=result.expires_in, - ) - - -@router.post("/refresh") -async def refresh_token( - request: RefreshTokenRequest, - interactor: FromDishka[RefreshTokenInteractor], -) -> AuthResponse: - result = await interactor.execute(request.refresh_token) - - return AuthResponse( - access_token=result.access_token, - refresh_token=result.refresh_token, - expires_in=result.expires_in, - ) diff --git a/tests/conftest.py b/tests/conftest.py index e9036be..1901153 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ from types import TracebackType from unittest.mock import AsyncMock +import jwt import pytest from dishka import ( AsyncContainer, @@ -15,49 +16,45 @@ ) from dishka.integrations.fastapi import FastapiProvider, setup_dishka from httpx import ASGITransport, AsyncClient -from redis.asyncio import Redis from sqlalchemy import URL from sqlalchemy.ext.asyncio import ( AsyncSession, create_async_engine, ) -from prodik.application.interfaces.password_hasher import PasswordHasher -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.password_hasher import PasswordHasher +from conode.application.interfaces.repositories import ( CompanyRepository, ContextRepository, EdgeRepository, GroupRepository, - LocalAuthorizationRepository, NodeAssociationRepository, NodeRepository, RolePermissionsRepository, RoleRepository, - SessionRepository, UserGrantRepository, UserRepository, ) -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, +from conode.application.interfaces.token_manager import ( + TokenManager, + UserMeta, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import RoleManagmentService -from prodik.bootstrap.api.run import create_app -from prodik.bootstrap.di.providers import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import RoleManagmentService +from conode.bootstrap.api.run import create_app +from conode.bootstrap.di.providers import ( ApplicationProvider, - CacheConnectionProvider, InfrastructureProvider, ) -from prodik.infrastructure.config import ( +from conode.domain.user import User, UserSystemRole +from conode.infrastructure.config import ( APIConfig, - CacheConfig, Config, DatabaseConfig, SecretsConfig, load_config, ) -from prodik.infrastructure.persistence import start_mapper +from conode.infrastructure.persistence import start_mapper from tests.factories.models import ( CompanyFactory, ContextFactory, @@ -69,6 +66,44 @@ ) +@dataclass +class MockTokenManager(TokenManager): + config: SecretsConfig + + def decode(self, token: str) -> UserMeta: + payload = jwt.decode( + token, + key=self.config.secret, + algorithms=["HS256"], + ) + role = UserSystemRole.USER + if "realm-admin" in payload["resource_access"]["realm-management"]["roles"]: + role = UserSystemRole.ADMIN + + return UserMeta( + email=payload["email"], + first_name=payload["given_name"], + last_name=payload["family_name"], + username=payload["preferred_username"], + system_role=role, + ) + + def encode(self, user: User, *, admin: bool = False) -> str: + return jwt.encode( + payload={ + "email": user.email.value, + "given_name": user.first_name.value, + "family_name": user.last_name.value, + "preferred_username": user.username.value, + "resource_access": { + "realm-management": {"roles": ["realm-admin" if admin else "user"]} + }, + }, + algorithm="HS256", + key=self.config.secret, + ) + + @pytest.fixture(scope="session") def config() -> Config: return load_config("test.config.toml") @@ -79,28 +114,6 @@ def startup() -> None: start_mapper() -@pytest.fixture(scope="session", autouse=True) -async def clear_cache(config: Config) -> AsyncIterator[None]: - yield - redis = Redis(host=config.cache.host, port=config.cache.port) - await redis.flushdb() - await redis.aclose() - - -@pytest.fixture -async def session_repository(container: AsyncContainer) -> SessionRepository: - async with container() as test_container: - return await test_container.get(SessionRepository) # type: ignore[no-any-return] - - -@pytest.fixture -async def local_authorization_repository( - container: AsyncContainer, -) -> LocalAuthorizationRepository: - async with container() as test_container: - return await test_container.get(LocalAuthorizationRepository) # type: ignore[no-any-return] - - @pytest.fixture async def node_repository( container: AsyncContainer, @@ -125,12 +138,6 @@ async def group_repository( return await test_container.get(GroupRepository) # type: ignore[no-any-return] -@pytest.fixture -async def access_token_manager(container: AsyncContainer) -> AccessTokenManager: - async with container() as test_container: - return await test_container.get(AccessTokenManager) # type: ignore[no-any-return] - - @pytest.fixture async def user_repository(container: AsyncContainer) -> UserRepository: async with container() as test_container: @@ -214,13 +221,8 @@ async def context_factory(container: AsyncContainer) -> ContextFactory: async def user_factory(container: AsyncContainer) -> UserFactory: async with container() as test_container: return UserFactory( - local_authorization_repository=await test_container.get( - LocalAuthorizationRepository - ), - refresh_token_manager=await test_container.get(RefreshTokenManager), - access_token_manager=await test_container.get(AccessTokenManager), + token_manager=await test_container.get(TokenManager), transaction_manager=await test_container.get(TransactionManager), - session_repository=await test_container.get(SessionRepository), password_hasher=await test_container.get(PasswordHasher), user_repository=await test_container.get(UserRepository), ) @@ -301,17 +303,19 @@ class TestConnectionProvider(Provider): async def provide_async_session(self) -> AsyncSession: return session + class TestSecretsProvider(Provider): + provides = provide_all(WithParents[MockTokenManager], scope=Scope.REQUEST) + container = make_async_container( FastapiProvider(), ApplicationProvider(), TestConnectionProvider(), InfrastructureProvider(), - CacheConnectionProvider(), + TestSecretsProvider(), context={ APIConfig: config.api, DatabaseConfig: config.database, SecretsConfig: config.secrets, - CacheConfig: config.cache, }, ) diff --git a/tests/e2e/authentication/__init__.py b/tests/e2e/authentication/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/e2e/authentication/test_login.py b/tests/e2e/authentication/test_login.py deleted file mode 100644 index 2ea94de..0000000 --- a/tests/e2e/authentication/test_login.py +++ /dev/null @@ -1,70 +0,0 @@ -from http import HTTPStatus - -import pytest -from dirty_equals import IsInt, IsPartialDataclass, IsPartialDict, IsStr -from httpx import AsyncClient - -from prodik.application.interfaces.repositories import SessionRepository -from tests.factories.models import UserFactory -from tests.factories.schemas import LoginRequestFactory - - -@pytest.mark.asyncio -async def test_login_ok( - transport: AsyncClient, - user_factory: UserFactory, - session_repository: SessionRepository, -) -> None: - user_factory_response = await user_factory.build() - - request = LoginRequestFactory.build( - email=user_factory_response.user.email.value, - password=user_factory_response.password, - ) - - response = await transport.post("/auth/login", json=request.model_dump()) - content = response.json() - - session = await session_repository.get_by_host_and_user_id( - user_factory_response.user.id, "127.0.0.1" - ) - - assert response.status_code == HTTPStatus.OK - assert session == IsPartialDataclass( - user_id=str(user_factory_response.user.id), - token=content["refresh_token"], - host="127.0.0.1", - ) - assert content == IsPartialDict( - access_token=IsStr, - refresh_token=IsStr, - expires_in=IsInt, - ) - - -@pytest.mark.asyncio -async def test_login_invalid_password( - transport: AsyncClient, - user_factory: UserFactory, - session_repository: SessionRepository, -) -> None: - user_factory_response = await user_factory.build() - - request = LoginRequestFactory.build( - email=user_factory_response.user.email.value, - password="", - ) - - response = await transport.post("/auth/login", json=request.model_dump()) - - session = await session_repository.get_by_token(user_factory_response.refresh_token) - - assert session is not None - assert response.status_code == HTTPStatus.UNAUTHORIZED - assert response.json() == IsPartialDict( - detail="Invalid email or password", - meta=[ - {"key": "email", "value": request.email}, - {"key": "password", "value": request.password}, - ], - ) diff --git a/tests/e2e/authentication/test_refresh_token.py b/tests/e2e/authentication/test_refresh_token.py deleted file mode 100644 index 167e8c9..0000000 --- a/tests/e2e/authentication/test_refresh_token.py +++ /dev/null @@ -1,68 +0,0 @@ -from http import HTTPStatus - -import pytest -from dirty_equals import IsInt, IsPartialDataclass, IsPartialDict, IsStr -from httpx import AsyncClient - -from prodik.application.interfaces.repositories import SessionRepository -from tests.factories.common import authorization_headers, generate_random_string -from tests.factories.models import UserFactory -from tests.factories.schemas import RefreshTokenRequestFactory - - -@pytest.mark.asyncio -async def test_refresh_token_ok( - transport: AsyncClient, - user_factory: UserFactory, - session_repository: SessionRepository, -) -> None: - user_factory_response = await user_factory.build() - - request = RefreshTokenRequestFactory.build( - refresh_token=user_factory_response.refresh_token - ) - - response = await transport.post( - "/auth/refresh", - headers=authorization_headers(user_factory_response.access_token), - json=request.model_dump(), - ) - content = response.json() - - session = await session_repository.get_by_host_and_user_id( - user_factory_response.user.id, "127.0.0.1" - ) - - assert response.status_code == HTTPStatus.OK - assert session == IsPartialDataclass( - user_id=str(user_factory_response.user.id), - token=content["refresh_token"], - host="127.0.0.1", - ) - assert content == IsPartialDict( - access_token=IsStr, - refresh_token=IsStr, - expires_in=IsInt, - ) - - -@pytest.mark.asyncio -async def test_refresh_token_without_active_session( - transport: AsyncClient, user_factory: UserFactory -) -> None: - user_factory_response = await user_factory.build() - - refresh_token = generate_random_string() - request = RefreshTokenRequestFactory.build(refresh_token=refresh_token) - - response = await transport.post( - "/auth/refresh", - headers=authorization_headers(user_factory_response.access_token), - json=request.model_dump(), - ) - - assert response.status_code == HTTPStatus.UNAUTHORIZED - assert response.json() == IsPartialDict( - detail="Session not found", - meta=[{"key": "refresh_token", "value": refresh_token}], - ) diff --git a/tests/e2e/authentication/test_register.py b/tests/e2e/authentication/test_register.py deleted file mode 100644 index e5515be..0000000 --- a/tests/e2e/authentication/test_register.py +++ /dev/null @@ -1,71 +0,0 @@ -from http import HTTPStatus - -import pytest -from dirty_equals import IsInt, IsPartialDataclass, IsPartialDict, IsStr -from httpx import AsyncClient - -from prodik.application.interfaces.repositories import ( - LocalAuthorizationRepository, - SessionRepository, -) -from prodik.application.interfaces.token_managers import AccessTokenManager -from tests.factories.models import UserFactory -from tests.factories.schemas import RegisterRequestFactory - - -@pytest.mark.asyncio -async def test_register_ok( - transport: AsyncClient, - session_repository: SessionRepository, - access_token_manager: AccessTokenManager, -) -> None: - request = RegisterRequestFactory.build() - - response = await transport.post("/auth/register", json=request.model_dump()) - content = response.json() - - session = await session_repository.get_by_token(content["refresh_token"]) - token_content = access_token_manager.decode(content["access_token"]) - - assert response.status_code == HTTPStatus.CREATED - - assert session == IsPartialDataclass( - user_id=token_content["user_id"], - token=content["refresh_token"], - host=IsStr, - ) - assert content == IsPartialDict( - access_token=IsStr, - refresh_token=IsStr, - expires_in=IsInt, - ) - - -@pytest.mark.asyncio -async def test_register_when_user_already_exists( - transport: AsyncClient, - user_factory: UserFactory, - local_authorization_repository: LocalAuthorizationRepository, -) -> None: - user_factory_response = await user_factory.build() - - request = RegisterRequestFactory.build( - email=user_factory_response.user.email.value, - username=user_factory_response.user.username.value, - ) - - response = await transport.post("/auth/register", json=request.model_dump()) - - local_authorizations = await local_authorization_repository.get_all_by_user_id( - user_factory_response.user.id - ) - - assert response.status_code == HTTPStatus.CONFLICT - assert len(local_authorizations) == 1 - assert response.json() == IsPartialDict( - detail="User with this username or email already exists", - meta=[ - {"key": "email", "value": user_factory_response.user.email.value}, - {"key": "username", "value": user_factory_response.user.username.value}, - ], - ) diff --git a/tests/e2e/company/test_register_company.py b/tests/e2e/company/test_register_company.py index b78e356..fba3489 100644 --- a/tests/e2e/company/test_register_company.py +++ b/tests/e2e/company/test_register_company.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict, IsStr from httpx import AsyncClient -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, RoleRepository, UserGrantRepository, diff --git a/tests/e2e/company/test_verify_company.py b/tests/e2e/company/test_verify_company.py index b3d1404..fb11883 100644 --- a/tests/e2e/company/test_verify_company.py +++ b/tests/e2e/company/test_verify_company.py @@ -3,7 +3,7 @@ import pytest from httpx import AsyncClient -from prodik.application.interfaces.repositories import CompanyRepository +from conode.application.interfaces.repositories import CompanyRepository from tests.factories.common import authorization_headers from tests.factories.models import CompanyFactory, UserFactory diff --git a/tests/e2e/context/test_create_context.py b/tests/e2e/context/test_create_context.py index fadf6b8..26a0655 100644 --- a/tests/e2e/context/test_create_context.py +++ b/tests/e2e/context/test_create_context.py @@ -4,8 +4,8 @@ from dirty_equals import IsPartialDataclass, IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import ContextRepository -from prodik.domain.context import ContextDescription, ContextName +from conode.application.interfaces.repositories import ContextRepository +from conode.domain.context import ContextDescription, ContextName from tests.factories.common import authorization_headers from tests.factories.models import CompanyFactory, UserFactory from tests.factories.schemas import CreateContextRequestFactory diff --git a/tests/e2e/context/test_delete_context.py b/tests/e2e/context/test_delete_context.py index e608156..9e3390a 100644 --- a/tests/e2e/context/test_delete_context.py +++ b/tests/e2e/context/test_delete_context.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import ContextRepository +from conode.application.interfaces.repositories import ContextRepository from tests.factories.common import authorization_headers from tests.factories.models import CompanyFactory, ContextFactory, UserFactory diff --git a/tests/e2e/edge/test_create_edge.py b/tests/e2e/edge/test_create_edge.py index 6676ced..149c4fb 100644 --- a/tests/e2e/edge/test_create_edge.py +++ b/tests/e2e/edge/test_create_edge.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDataclass, IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import EdgeRepository +from conode.application.interfaces.repositories import EdgeRepository from tests.factories.common import authorization_headers from tests.factories.models import ( CompanyFactory, diff --git a/tests/e2e/edge/test_delete_edge.py b/tests/e2e/edge/test_delete_edge.py index f86811e..3f79c8d 100644 --- a/tests/e2e/edge/test_delete_edge.py +++ b/tests/e2e/edge/test_delete_edge.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import EdgeRepository +from conode.application.interfaces.repositories import EdgeRepository from tests.factories.common import authorization_headers from tests.factories.models import ( CompanyFactory, diff --git a/tests/e2e/edge/test_manipulate_edge_weight.py b/tests/e2e/edge/test_manipulate_edge_weight.py index 29f1184..1db7c98 100644 --- a/tests/e2e/edge/test_manipulate_edge_weight.py +++ b/tests/e2e/edge/test_manipulate_edge_weight.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDataclass, IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import EdgeRepository +from conode.application.interfaces.repositories import EdgeRepository from tests.factories.common import authorization_headers from tests.factories.models import ( CompanyFactory, diff --git a/tests/e2e/group/test_create_group.py b/tests/e2e/group/test_create_group.py index 0d46f5a..a9e0522 100644 --- a/tests/e2e/group/test_create_group.py +++ b/tests/e2e/group/test_create_group.py @@ -5,7 +5,7 @@ from dirty_equals import IsPartialDict, IsStr from httpx import AsyncClient -from prodik.application.interfaces.repositories import GroupRepository +from conode.application.interfaces.repositories import GroupRepository from tests.factories.common import authorization_headers from tests.factories.models import CompanyFactory, GroupFactory, UserFactory from tests.factories.schemas import CreateGroupRequestFactory diff --git a/tests/e2e/group/test_delete_group.py b/tests/e2e/group/test_delete_group.py index 8cb99f7..c85ebe2 100644 --- a/tests/e2e/group/test_delete_group.py +++ b/tests/e2e/group/test_delete_group.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( GroupRepository, NodeAssociationRepository, ) diff --git a/tests/e2e/helpers/asserts.py b/tests/e2e/helpers/asserts.py index 469c5cb..4ef436e 100644 --- a/tests/e2e/helpers/asserts.py +++ b/tests/e2e/helpers/asserts.py @@ -1,12 +1,12 @@ from typing import Any -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, RoleRepository, UserGrantRepository, ) -from prodik.domain.role import RoleName -from prodik.domain.user import User +from conode.domain.role import RoleName +from conode.domain.user import User async def assert_user_have_correct_rights( diff --git a/tests/e2e/node/test_attach_node.py b/tests/e2e/node/test_attach_node.py index 072bf70..c2d390f 100644 --- a/tests/e2e/node/test_attach_node.py +++ b/tests/e2e/node/test_attach_node.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( NodeAssociationRepository, ) from tests.factories.common import authorization_headers diff --git a/tests/e2e/node/test_create_node.py b/tests/e2e/node/test_create_node.py index ff4f822..e295399 100644 --- a/tests/e2e/node/test_create_node.py +++ b/tests/e2e/node/test_create_node.py @@ -4,11 +4,11 @@ from dirty_equals import IsPartialDataclass, IsPartialDict, IsStr from httpx import AsyncClient -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( NodeAssociationRepository, NodeRepository, ) -from prodik.domain.node import NodeDescription, NodeName +from conode.domain.node import NodeDescription, NodeName from tests.factories.common import authorization_headers from tests.factories.models import CompanyFactory, GroupFactory, UserFactory from tests.factories.schemas import CreateNodeRequestFactory diff --git a/tests/e2e/node/test_delete_node.py b/tests/e2e/node/test_delete_node.py index b967c29..1251b02 100644 --- a/tests/e2e/node/test_delete_node.py +++ b/tests/e2e/node/test_delete_node.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( NodeAssociationRepository, NodeRepository, ) diff --git a/tests/e2e/node/test_detach_node.py b/tests/e2e/node/test_detach_node.py index f651b87..d5db0bf 100644 --- a/tests/e2e/node/test_detach_node.py +++ b/tests/e2e/node/test_detach_node.py @@ -4,7 +4,7 @@ from dirty_equals import IsPartialDataclass, IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import NodeAssociationRepository +from conode.application.interfaces.repositories import NodeAssociationRepository from tests.factories.common import authorization_headers from tests.factories.models import ( CompanyFactory, diff --git a/tests/e2e/node/test_update_node.py b/tests/e2e/node/test_update_node.py index 020b9cc..3a3a504 100644 --- a/tests/e2e/node/test_update_node.py +++ b/tests/e2e/node/test_update_node.py @@ -4,8 +4,8 @@ from dirty_equals import IsPartialDataclass, IsPartialDict from httpx import AsyncClient -from prodik.application.interfaces.repositories import NodeRepository -from prodik.domain.node import NodeDescription, NodeName +from conode.application.interfaces.repositories import NodeRepository +from conode.domain.node import NodeDescription, NodeName from tests.factories.common import authorization_headers from tests.factories.models import ( CompanyFactory, diff --git a/tests/e2e/user/test_update_current_profile.py b/tests/e2e/user/test_update_current_profile.py index d45fe3a..d807b42 100644 --- a/tests/e2e/user/test_update_current_profile.py +++ b/tests/e2e/user/test_update_current_profile.py @@ -4,8 +4,8 @@ from dirty_equals import IsPartialDataclass from httpx import AsyncClient -from prodik.application.interfaces.repositories import UserRepository -from prodik.domain.user import Bio, Email, FirstName, LastName, Username +from conode.application.interfaces.repositories import UserRepository +from conode.domain.user import Bio, FirstName, LastName, Username from tests.factories.common import authorization_headers from tests.factories.models import UserFactory from tests.factories.schemas import ( @@ -35,6 +35,5 @@ async def test_update_current_user_profile_ok( first_name=FirstName(request.first_name), last_name=LastName(request.last_name), username=Username(request.username), - email=Email(request.email), bio=Bio(request.bio), ) diff --git a/tests/factories/common.py b/tests/factories/common.py index 13e36e0..ec5c616 100644 --- a/tests/factories/common.py +++ b/tests/factories/common.py @@ -29,4 +29,4 @@ def generate_random_ip() -> str: def authorization_headers(access_token: str) -> dict[str, str]: - return {"Authorization": f"Bearer {access_token}"} + return {"X-Forwarded-Access-Token": access_token} diff --git a/tests/factories/models/company.py b/tests/factories/models/company.py index c966a43..a3d94c0 100644 --- a/tests/factories/models/company.py +++ b/tests/factories/models/company.py @@ -1,18 +1,18 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( CompanyRepository, RolePermissionsRepository, RoleRepository, UserGrantRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import RoleManagmentService -from prodik.domain.company import Company, CompanyId -from prodik.domain.grant import UserGrant, UserGrantId -from prodik.domain.role import EntityType, PermissionType -from prodik.domain.user import User +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import RoleManagmentService +from conode.domain.company import Company, CompanyId +from conode.domain.grant import UserGrant, UserGrantId +from conode.domain.role import EntityType, PermissionType +from conode.domain.user import User from tests.factories.common import generate_random_string diff --git a/tests/factories/models/context.py b/tests/factories/models/context.py index 831d464..7f314a2 100644 --- a/tests/factories/models/context.py +++ b/tests/factories/models/context.py @@ -1,10 +1,10 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ContextRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.company import Company -from prodik.domain.context import Context, ContextId +from conode.application.interfaces.repositories import ContextRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.company import Company +from conode.domain.context import Context, ContextId from tests.factories.common import generate_random_string diff --git a/tests/factories/models/edge.py b/tests/factories/models/edge.py index 09b2b58..4de9b25 100644 --- a/tests/factories/models/edge.py +++ b/tests/factories/models/edge.py @@ -1,12 +1,12 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import EdgeRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.company import Company -from prodik.domain.context import Context -from prodik.domain.edge import Edge, EdgeId -from prodik.domain.node import Node +from conode.application.interfaces.repositories import EdgeRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.company import Company +from conode.domain.context import Context +from conode.domain.edge import Edge, EdgeId +from conode.domain.node import Node @dataclass diff --git a/tests/factories/models/group.py b/tests/factories/models/group.py index c7b2ec7..f10d37b 100644 --- a/tests/factories/models/group.py +++ b/tests/factories/models/group.py @@ -1,10 +1,10 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import GroupRepository -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.company import Company -from prodik.domain.group import Group, GroupId +from conode.application.interfaces.repositories import GroupRepository +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.company import Company +from conode.domain.group import Group, GroupId from tests.factories.common import generate_random_string diff --git a/tests/factories/models/node.py b/tests/factories/models/node.py index 2cfae04..9a1cccf 100644 --- a/tests/factories/models/node.py +++ b/tests/factories/models/node.py @@ -1,14 +1,14 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( NodeAssociationRepository, NodeRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.company import Company -from prodik.domain.group import Group -from prodik.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.company import Company +from conode.domain.group import Group +from conode.domain.node import Node, NodeAssociation, NodeAssociationId, NodeId from tests.factories.common import generate_random_string diff --git a/tests/factories/models/role.py b/tests/factories/models/role.py index 40e0303..424a01f 100644 --- a/tests/factories/models/role.py +++ b/tests/factories/models/role.py @@ -1,18 +1,18 @@ from dataclasses import dataclass -from prodik.application.interfaces.repositories import ( +from conode.application.interfaces.repositories import ( RolePermissionsRepository, RoleRepository, ) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.application.services import ( +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.application.services import ( RoleManagmentService, RoleManagmentServiceResponse, ) -from prodik.domain.company import Company -from prodik.domain.context import Context -from prodik.domain.group import Group -from prodik.domain.role import EntityType, PermissionType +from conode.domain.company import Company +from conode.domain.context import Context +from conode.domain.group import Group +from conode.domain.role import EntityType, PermissionType from tests.factories.common import generate_random_string entity_types_map = { diff --git a/tests/factories/models/user.py b/tests/factories/models/user.py index 8d9ae90..06ea098 100644 --- a/tests/factories/models/user.py +++ b/tests/factories/models/user.py @@ -1,24 +1,11 @@ from dataclasses import dataclass from uuid import uuid4 -from prodik.application.interfaces.password_hasher import PasswordHasher -from prodik.application.interfaces.repositories import ( - LocalAuthorizationRepository, - SessionRepository, - UserRepository, -) -from prodik.application.interfaces.token_managers import ( - AccessTokenManager, - RefreshTokenManager, -) -from prodik.application.interfaces.transaction_manager import TransactionManager -from prodik.domain.authorization import ( - LocalAuthorization, - LocalAuthorizationId, - Session, - SessionId, -) -from prodik.domain.user import User, UserId, UserSystemRole +from conode.application.interfaces.password_hasher import PasswordHasher +from conode.application.interfaces.repositories import UserRepository +from conode.application.interfaces.token_manager import TokenManager +from conode.application.interfaces.transaction_manager import TransactionManager +from conode.domain.user import User, UserId, UserSystemRole from tests.factories.common import generate_random_string @@ -26,17 +13,12 @@ class UserFactoryResponse: user: User access_token: str - refresh_token: str - password: str @dataclass class UserFactory: - local_authorization_repository: LocalAuthorizationRepository - refresh_token_manager: RefreshTokenManager - access_token_manager: AccessTokenManager + token_manager: TokenManager transaction_manager: TransactionManager - session_repository: SessionRepository user_repository: UserRepository password_hasher: PasswordHasher @@ -60,31 +42,9 @@ async def build(self, *, admin: bool = False) -> UserFactoryResponse: if admin: user.system_role = UserSystemRole.ADMIN - access_token = self.access_token_manager.encode(user) - refresh_token = self.refresh_token_manager.encode() - password = generate_random_string() - hashed_password = self.password_hasher.hash(password) - - authorization = LocalAuthorization.new( - local_authorization_id=LocalAuthorizationId(uuid4()), - password=hashed_password, - user=user, - ) - - session = Session.new( - session_id=SessionId(uuid4()), - host="127.0.0.1", - token=refresh_token, - user=user, - ) - await self.user_repository.create(user) - await self.local_authorization_repository.create(authorization) - await self.session_repository.create(session) return UserFactoryResponse( user=user, - access_token=access_token.token, - refresh_token=refresh_token, - password=password, + access_token=self.token_manager.encode(user), ) diff --git a/tests/factories/schemas/__init__.py b/tests/factories/schemas/__init__.py index b5c7332..aa4078b 100644 --- a/tests/factories/schemas/__init__.py +++ b/tests/factories/schemas/__init__.py @@ -1,8 +1,3 @@ -from .authentication import ( - LoginRequestFactory, - RefreshTokenRequestFactory, - RegisterRequestFactory, -) from .company import RegisterCompanyRequestFactory from .context import CreateContextRequestFactory from .edge import CreateEdgeRequestFactory @@ -13,7 +8,6 @@ UpdateNodeRequestFactory, ) from .user import ( - UpdateCurrentUserPasswordRequestFactory, UpdateCurrentUserProfileRequestFactory, ) @@ -23,11 +17,7 @@ "CreateEdgeRequestFactory", "CreateGroupRequestFactory", "CreateNodeRequestFactory", - "LoginRequestFactory", - "RefreshTokenRequestFactory", "RegisterCompanyRequestFactory", - "RegisterRequestFactory", - "UpdateCurrentUserPasswordRequestFactory", "UpdateCurrentUserProfileRequestFactory", "UpdateNodeRequestFactory", ) diff --git a/tests/factories/schemas/authentication.py b/tests/factories/schemas/authentication.py deleted file mode 100644 index 4de7f4c..0000000 --- a/tests/factories/schemas/authentication.py +++ /dev/null @@ -1,17 +0,0 @@ -from polyfactory.factories.pydantic_factory import ModelFactory - -from prodik.presentation.schemas.auth import ( - LoginRequest, - RefreshTokenRequest, - RegisterRequest, -) - - -class RegisterRequestFactory(ModelFactory[RegisterRequest]): - __use_examples__ = True - - -class RefreshTokenRequestFactory(ModelFactory[RefreshTokenRequest]): ... - - -class LoginRequestFactory(ModelFactory[LoginRequest]): ... diff --git a/tests/factories/schemas/company.py b/tests/factories/schemas/company.py index 86aa767..269d2d6 100644 --- a/tests/factories/schemas/company.py +++ b/tests/factories/schemas/company.py @@ -1,6 +1,6 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.company import RegisterCompanyRequest +from conode.presentation.schemas.company import RegisterCompanyRequest class RegisterCompanyRequestFactory(ModelFactory[RegisterCompanyRequest]): diff --git a/tests/factories/schemas/context.py b/tests/factories/schemas/context.py index 280bca9..dd24a3e 100644 --- a/tests/factories/schemas/context.py +++ b/tests/factories/schemas/context.py @@ -1,6 +1,6 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.context import CreateContextRequest +from conode.presentation.schemas.context import CreateContextRequest class CreateContextRequestFactory(ModelFactory[CreateContextRequest]): diff --git a/tests/factories/schemas/edge.py b/tests/factories/schemas/edge.py index e3ef873..740e006 100644 --- a/tests/factories/schemas/edge.py +++ b/tests/factories/schemas/edge.py @@ -1,6 +1,6 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.edge import CreateEdgeRequest +from conode.presentation.schemas.edge import CreateEdgeRequest class CreateEdgeRequestFactory(ModelFactory[CreateEdgeRequest]): ... diff --git a/tests/factories/schemas/group.py b/tests/factories/schemas/group.py index a81dd88..7b34e74 100644 --- a/tests/factories/schemas/group.py +++ b/tests/factories/schemas/group.py @@ -1,6 +1,6 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.group import CreateGroupRequest +from conode.presentation.schemas.group import CreateGroupRequest class CreateGroupRequestFactory(ModelFactory[CreateGroupRequest]): diff --git a/tests/factories/schemas/node.py b/tests/factories/schemas/node.py index dcf4cfe..f84b51b 100644 --- a/tests/factories/schemas/node.py +++ b/tests/factories/schemas/node.py @@ -1,6 +1,6 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.node import ( +from conode.presentation.schemas.node import ( AttachNodeRequest, CreateNodeRequest, UpdateNodeRequest, diff --git a/tests/factories/schemas/user.py b/tests/factories/schemas/user.py index 35b29d3..4f95e88 100644 --- a/tests/factories/schemas/user.py +++ b/tests/factories/schemas/user.py @@ -1,16 +1,10 @@ from polyfactory.factories.pydantic_factory import ModelFactory -from prodik.presentation.schemas.user import ( - UpdateCurrentUserPasswordRequest, +from conode.presentation.schemas.user import ( UpdateCurrentUserProfileRequest, ) -class UpdateCurrentUserPasswordRequestFactory( - ModelFactory[UpdateCurrentUserPasswordRequest] -): ... - - class UpdateCurrentUserProfileRequestFactory( ModelFactory[UpdateCurrentUserProfileRequest] ): ... diff --git a/uv.lock b/uv.lock index 010b06b..4e2e906 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,8 @@ requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.15'", "python_full_version == '3.14.*'", - "python_full_version < '3.14'", + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", ] [[package]] @@ -14,7 +15,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } @@ -45,7 +46,7 @@ name = "anyio" version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] @@ -217,6 +218,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/d7/8fb3044eaef08a310acfe23dae9a8e2e07d305edc29a53497e52bc76eca7/asyncpg-0.31.0-cp314-cp314t-win_amd64.whl", hash = "sha256:bd4107bb7cdd0e9e65fae66a62afd3a249663b844fa34d479f6d5b3bef9c04c3", size = 706062, upload-time = "2025-11-24T23:26:44.086Z" }, ] +[[package]] +name = "attrs" +version = "21.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/77/ebb15fc26d0f815839ecd897b919ed6d85c050feeb83e100e020df9153d2/attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd", size = 201839, upload-time = "2021-12-29T13:15:09.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/be/7abce643bfdf8ca01c48afa2ddf8308c2308b0c3b239a44e57d020afa0ef/attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", size = 60567, upload-time = "2021-12-29T13:15:06.703Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + [[package]] name = "backports-asyncio-runner" version = "1.2.0" @@ -226,19 +253,61 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, ] +[[package]] +name = "bandit" +version = "1.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "stevedore", version = "5.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "stevedore", version = "5.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" }, +] + +[[package]] +name = "black" +version = "22.12.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "mypy-extensions", marker = "python_full_version < '3.14'" }, + { name = "pathspec", marker = "python_full_version < '3.14'" }, + { name = "platformdirs", marker = "python_full_version < '3.14'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/59/e873cc6807fb62c11131e5258ca15577a3b7452abad08dc49286cf8245e8/black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f", size = 553112, upload-time = "2022-12-09T15:57:04.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/d9/60852a6fc2f85374db20a9767dacfe50c2172eb8388f46018c8daf836995/black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d", size = 1556665, upload-time = "2022-12-09T16:04:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/975782465cc6b514f2c972421e29b933dfbb51d4a95948a4e0e94f36ea38/black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351", size = 1205632, upload-time = "2022-12-09T16:14:38.465Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e0/6aa02d14785c4039b38bfed6f9ee28a952b2d101c64fc97b15811fa8bd04/black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f", size = 1536577, upload-time = "2022-12-09T16:04:12.721Z" }, + { url = "https://files.pythonhosted.org/packages/4c/49/420dcfccba3215dc4e5790fa47572ef14129df1c5e95dd87b5ad30211b01/black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4", size = 1209873, upload-time = "2022-12-09T16:14:40.318Z" }, + { url = "https://files.pythonhosted.org/packages/0c/51/1f7f93c0555eaf4cbb628e26ba026e3256174a45bd9397ff1ea7cf96bad5/black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf", size = 167343, upload-time = "2022-12-09T15:57:02.229Z" }, +] + [[package]] name = "black" version = "26.3.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "mypy-extensions", marker = "python_full_version >= '3.14'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "pathspec", marker = "python_full_version >= '3.14'" }, + { name = "platformdirs", marker = "python_full_version >= '3.14'" }, + { name = "pytokens", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } wheels = [ @@ -270,6 +339,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, ] +[[package]] +name = "boltons" +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/1f/6c0608d86e0fc77c982a2923ece80eef85f091f2332fc13cbce41d70d502/boltons-21.0.0.tar.gz", hash = "sha256:65e70a79a731a7fe6e98592ecfb5ccf2115873d01dbc576079874629e5c90f13", size = 180201, upload-time = "2021-05-17T01:20:17.802Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/a7/1a31561d10a089fcb46fe286766dd4e053a12f6e23b4fd1c26478aff2475/boltons-21.0.0-py2.py3-none-any.whl", hash = "sha256:b9bb7b58b2b420bbe11a6025fdef6d3e5edc9f76a42fb467afe7ca212ef9948b", size = 193723, upload-time = "2021-05-17T01:20:20.023Z" }, +] + +[[package]] +name = "bracex" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/f5/4473ad9b48cd0420a2d762a3750fa0e078e23e060b1af72662e5987e5530/bracex-3.0.tar.gz", hash = "sha256:b73f718d6bd98d8419e45df02426c86e9967c179949f779340d6c3a8c83b9111", size = 43162, upload-time = "2026-06-30T00:43:35.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/2e/68781b78e764e5ccc4af1e3d27e060069c73af90234853fa80000e7ee79d/bracex-3.0-py3-none-any.whl", hash = "sha256:3833e61c2f092d5aa0468fa2e6c6e990a306185abf763b6d122f0158e59c58a5", size = 11738, upload-time = "2026-06-30T00:43:34.196Z" }, +] + [[package]] name = "certifi" version = "2026.4.22" @@ -361,18 +448,138 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/81/8e983840c6e5b93b33c2ba81aa3d52c2e42f0e9a690ce7607a2e61da4a5c/charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a", size = 322240, upload-time = "2026-07-07T14:32:36.236Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/b4319dc3229d8272fba305e206fc0a148e2de8d4087917ce62ae6382f359/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616", size = 216475, upload-time = "2026-07-07T14:32:38.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/6c99c1b3e6b8bf730e1bc809b9a2608f224145069114c479a2e9e1494346/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209", size = 238670, upload-time = "2026-07-07T14:32:39.658Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f4/ffbb83546e1f198ecc70ecd372b65cf2b50f9068b380abd67640f17a8e18/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99", size = 233476, upload-time = "2026-07-07T14:32:41.155Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5f/b98b8da398637b551e427e7be922bdec19177dc54d6811dcdaa503f23aac/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8", size = 223817, upload-time = "2026-07-07T14:32:42.592Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/a276bb2e66243072a3fd06fdcab9cbb61a305b02143d70d2bda21d888fa8/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b", size = 207974, upload-time = "2026-07-07T14:32:44.258Z" }, + { url = "https://files.pythonhosted.org/packages/5e/be/7ee4453d7e88dfbc4104ccd34900b9f2c7c17dac22881865fe0e82424a25/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2", size = 221655, upload-time = "2026-07-07T14:32:45.64Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/181c652953eb5276d198f375b1dd641047392050098100a3a02d6534f657/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9", size = 219229, upload-time = "2026-07-07T14:32:47.376Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e7/aaf6da33fc9f4691cda8f7efbc9f69179d3d39ec8a4799baf273ee1d8db0/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15", size = 209704, upload-time = "2026-07-07T14:32:48.855Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/f2fb3bd3a73be48b173ee0c6aa8d2497af97d5663a8c4c4b491de4c62f7a/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d", size = 226243, upload-time = "2026-07-07T14:32:50.239Z" }, + { url = "https://files.pythonhosted.org/packages/c4/02/c57a22739fe05246b0b5783b3bfb6afaac4eebb46f3ececdfb2f048f780e/charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381", size = 150935, upload-time = "2026-07-07T14:32:51.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/8d/ca39a7559a4797505530d084fd3a49a2c959efbbbff146302fb7be4e3b35/charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee", size = 162314, upload-time = "2026-07-07T14:32:53.193Z" }, + { url = "https://files.pythonhosted.org/packages/01/da/a44bd7a13d426e69e4894557106cd58669097bfad4a8681123b618fbfc5d/charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419", size = 153075, upload-time = "2026-07-07T14:32:54.554Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/85ec501f206fb049259288c1f3506e53876937fb00edb47009348e66756b/charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5", size = 317075, upload-time = "2026-07-07T14:32:56.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/2a5385192e67175f7d8bd5ce4f57c24bc956439adeae5c13a99aa28a53d1/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2", size = 213837, upload-time = "2026-07-07T14:32:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/03ddc7da576d814fe0a36dd1f0fd3258e95404b4b2e3c026b7923d7e133f/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a", size = 235503, upload-time = "2026-07-07T14:32:59.205Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/de0229a7ef40f6f9d28a837eebf4ec47bdca5dab4e900c84f22919af636a/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29", size = 229944, upload-time = "2026-07-07T14:33:00.803Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/49b9060e8418b14fb5cba9cf6bfb383111e2538a03a1fb18e66a95aeb3d5/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c", size = 221276, upload-time = "2026-07-07T14:33:02.199Z" }, + { url = "https://files.pythonhosted.org/packages/44/95/80282cce0fae9c3061203d723ee87da996aed79679e65d8935050ee7ca1f/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b", size = 205260, upload-time = "2026-07-07T14:33:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/0c/74/2f62c8821b969ea3bd67cc2e6976834f48ca5d12664d2559ebcd9bcfbed7/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db", size = 217786, upload-time = "2026-07-07T14:33:05.12Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8d/feabb82cb49fcad14515b1d7d1ca4787b0da7fc723a212bf89bc9e0fac52/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993", size = 216798, upload-time = "2026-07-07T14:33:06.629Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ff/c946d63bc3786d5b84d960b0f7ab7e25b828486a946b5aa997625bcaf6a6/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da", size = 206429, upload-time = "2026-07-07T14:33:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/af/ba/5e5007c370702f85d2ef75791fac7943ed41e080364a673b20142e430e3e/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3", size = 223066, upload-time = "2026-07-07T14:33:09.783Z" }, + { url = "https://files.pythonhosted.org/packages/83/d5/9096aa3cf532dfad237861544eb47a0f20d5adbf1039760fed8eaae935d9/charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d", size = 150456, upload-time = "2026-07-07T14:33:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a1/e29995109e455dc8eff8d0fac6ae509be39561318a7cfeac5d33ad029213/charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1", size = 161410, upload-time = "2026-07-07T14:33:12.743Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8d/1569f4d0032d6ba2a4fe4591c35bf87868c600c41a71eb5c2e1ffa8464c2/charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec", size = 152649, upload-time = "2026-07-07T14:33:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", size = 319300, upload-time = "2026-07-07T14:33:15.666Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", size = 215802, upload-time = "2026-07-07T14:33:17.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", size = 237171, upload-time = "2026-07-07T14:33:18.576Z" }, + { url = "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", size = 233075, upload-time = "2026-07-07T14:33:20.084Z" }, + { url = "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", size = 224256, upload-time = "2026-07-07T14:33:21.747Z" }, + { url = "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", size = 208784, upload-time = "2026-07-07T14:33:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", size = 219928, upload-time = "2026-07-07T14:33:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", size = 218489, upload-time = "2026-07-07T14:33:26.42Z" }, + { url = "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", size = 210267, upload-time = "2026-07-07T14:33:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", size = 226030, upload-time = "2026-07-07T14:33:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/42/6dbc00b8cd16011691203e33570fa42ed5746599a2e878112d16eab403a3/charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642", size = 151185, upload-time = "2026-07-07T14:33:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/80/cc/f920afd1a23c58ccd53c1d36085a71893a4737ff5e66e0371efab6809850/charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0", size = 162557, upload-time = "2026-07-07T14:33:32.176Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/0386d43a261ff4e4b30c5857af7df877254b46bec7b9d1b74b6bf969a90b/charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2", size = 152665, upload-time = "2026-07-07T14:33:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, + { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, + { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, + { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + [[package]] name = "click" version = "8.3.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "python_full_version < '3.14' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] +[[package]] +name = "click-option-group" +version = "0.5.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/ff/d291d66595b30b83d1cb9e314b2c9be7cfc7327d4a0d40a15da2416ea97b/click_option_group-0.5.9.tar.gz", hash = "sha256:f94ed2bc4cf69052e0f29592bd1e771a1789bd7bfc482dd0bc482134aff95823", size = 22222, upload-time = "2025-10-09T09:38:01.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/45/54bb2d8d4138964a94bef6e9afe48b0be4705ba66ac442ae7d8a8dc4ffef/click_option_group-0.5.9-py3-none-any.whl", hash = "sha256:ad2599248bd373e2e19bec5407967c3eec1d0d4fc4a5e77b08a0481e75991080", size = 11553, upload-time = "2025-10-09T09:38:00.066Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -382,6 +589,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "conode" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "alembic" }, + { name = "argon2-cffi" }, + { name = "asyncpg" }, + { name = "bandit" }, + { name = "cryptography" }, + { name = "dishka" }, + { name = "fastapi" }, + { name = "pydantic", extra = ["email"] }, + { name = "pyjwt" }, + { name = "semgrep", version = "0.112.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "semgrep", version = "1.156.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "sqlalchemy" }, + { name = "structlog" }, + { name = "uvicorn" }, + { name = "zizmor" }, +] + +[package.optional-dependencies] +dev = [ + { name = "datamodel-code-generator" }, + { name = "dirty-equals" }, + { name = "httpx" }, + { name = "mypy" }, + { name = "polyfactory" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-xdist" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "alembic", specifier = "==1.18.4" }, + { name = "argon2-cffi", specifier = "==25.1.0" }, + { name = "asyncpg", specifier = "==0.31.0" }, + { name = "bandit", specifier = ">=1.9.4" }, + { name = "cryptography", specifier = "==46.0.5" }, + { name = "datamodel-code-generator", marker = "extra == 'dev'", specifier = "==0.54.0" }, + { name = "dirty-equals", marker = "extra == 'dev'", specifier = "==0.11" }, + { name = "dishka", specifier = "==1.7.2" }, + { name = "fastapi", specifier = "==0.128.0" }, + { name = "httpx", marker = "extra == 'dev'", specifier = "==0.28.1" }, + { name = "mypy", marker = "extra == 'dev'" }, + { name = "polyfactory", marker = "extra == 'dev'", specifier = "==3.3.0" }, + { name = "pydantic", extras = ["email"], specifier = "==2.13.2" }, + { name = "pyjwt", specifier = "==2.11.0" }, + { name = "pytest", marker = "extra == 'dev'" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = "==1.3.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.1.0" }, + { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.8.0" }, + { name = "ruff", marker = "extra == 'dev'" }, + { name = "semgrep", specifier = ">=0.112.1" }, + { name = "sqlalchemy", specifier = "==2.0.46" }, + { name = "structlog", specifier = "==25.5.0" }, + { name = "uvicorn", specifier = "==0.40.0" }, + { name = "zizmor", specifier = ">=1.27.0" }, +] +provides-extras = ["dev"] + [[package]] name = "coverage" version = "7.14.2" @@ -482,7 +755,7 @@ wheels = [ [package.optional-dependencies] toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.11'" }, ] [[package]] @@ -551,21 +824,32 @@ version = "0.54.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, - { name = "black" }, + { name = "black", version = "22.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "black", version = "26.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "genson" }, { name = "inflect" }, { name = "isort" }, { name = "jinja2" }, - { name = "packaging" }, + { name = "packaging", version = "21.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "pydantic" }, { name = "pyyaml" }, - { name = "tomli", marker = "python_full_version < '3.12'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/43/2640cd5293fb5430528166908d6439cf3321bc1c54de5fe58ef100b143a1/datamodel_code_generator-0.54.0.tar.gz", hash = "sha256:2b183598d049e265146a8224c35d1bb96a80a641ea8ecd2a82e6a0e97b56da6b", size = 824327, upload-time = "2026-02-14T16:19:05.347Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2b/d9/fd646ea4ae48e374817b2750f5e678a5bf6e10d8924f09cf4cce86a81607/datamodel_code_generator-0.54.0-py3-none-any.whl", hash = "sha256:3156df7a7e8fa5a7c9a6d50836e5ba5abe0532f6b71eee6d73a0c8e1fb5b7e47", size = 261838, upload-time = "2026-02-14T16:19:03.298Z" }, ] +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +] + [[package]] name = "dirty-equals" version = "0.11" @@ -580,7 +864,7 @@ name = "dishka" version = "1.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/d7/1be31f5ef32387059190353f9fa493ff4d07a1c75fa856c7566ca45e0800/dishka-1.7.2.tar.gz", hash = "sha256:47d4cb5162b28c61bf5541860e605ed5eaf5c667122299c7ef657c86fc8d5a49", size = 68132, upload-time = "2025-09-24T21:23:05.135Z" } wheels = [ @@ -609,12 +893,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, +] + [[package]] name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -630,6 +930,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, ] +[[package]] +name = "face" +version = "26.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boltons" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/fd/f84f0600bd72953d5a322f0dedbd4f900e2cedab718e6b6a093ae2d16aae/face-26.0.1.tar.gz", hash = "sha256:8183d94bc248baaea855a9f8445f97a22a9988908e60abddccc6e251da77c4c6", size = 51754, upload-time = "2026-06-17T23:14:39.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/24/0159c48d19c8b05e6969ee809bbbecc5a5c863f7e38c9327e2c63cb06f0f/face-26.0.1-py3-none-any.whl", hash = "sha256:ab0a83c37c9789dce658a67a9a80eafaa113c9ec37c5a9d950ff5480542a062d", size = 57572, upload-time = "2026-06-17T23:14:38.711Z" }, +] + [[package]] name = "faker" version = "40.18.0" @@ -666,6 +978,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470, upload-time = "2024-05-15T22:08:47.056Z" }, ] +[[package]] +name = "glom" +version = "22.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "attrs", version = "21.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "boltons", marker = "python_full_version < '3.14'" }, + { name = "face", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/d1/69432deefa6f5283ec75b246d0540097ae26f618b915519ee3824c4c5dd6/glom-22.1.0.tar.gz", hash = "sha256:1510c6587a8f9c64a246641b70033cbc5ebde99f02ad245693678038e821aeb5", size = 189738, upload-time = "2022-01-24T09:34:04.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/e8/68e274b2a30e1fdfd25bdc27194382be3f233929c8f727c0440d58ac074f/glom-22.1.0-py2.py3-none-any.whl", hash = "sha256:5339da206bf3532e01a83a35aca202960ea885156986d190574b779598e9e772", size = 100687, upload-time = "2022-01-24T09:34:02.391Z" }, +] + +[[package]] +name = "glom" +version = "25.12.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +dependencies = [ + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "boltons", marker = "python_full_version >= '3.14'" }, + { name = "face", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/74/8387f95565ba7c30cd152a585b275ebb9a834d1d32782425c5d2fe0a102c/glom-25.12.0.tar.gz", hash = "sha256:1ae7da88be3693df40ad27bdf57a765a55c075c86c971bcddd67927403eb0069", size = 196128, upload-time = "2025-12-29T06:29:07.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/e6/4129d9a3baa72d747533bb33376543ccadd9a7f9944e5a6e3ae2e245f5d6/glom-25.12.0-py3-none-any.whl", hash = "sha256:b9f21e77f71a6576a43864e85066b8cc3f0f778d0d50961563f8981377a6dcb1", size = 103295, upload-time = "2025-12-29T06:29:06.074Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035, upload-time = "2026-05-07T08:04:49.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, +] + [[package]] name = "greenlet" version = "3.5.0" @@ -729,92 +1089,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] -[[package]] -name = "hiredis" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/97/d6/9bef6dc3052c168c93fbf7e6c0f2b12c45f0f741a2d30fd919096774343a/hiredis-3.3.1.tar.gz", hash = "sha256:da6f0302360e99d32bc2869772692797ebadd536e1b826d0103c72ba49d38698", size = 89101, upload-time = "2026-03-16T15:21:08.092Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d0/fe8d86b94a9c70100c4a9402d301422515aec4055b982b8c189f0c94a677/hiredis-3.3.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:f525734382a47f9828c9d6a1501522c78d5935466d8e2be1a41ba40ca5bb922b", size = 81825, upload-time = "2026-03-16T15:19:05.397Z" }, - { url = "https://files.pythonhosted.org/packages/80/76/88edf234200e9592c19f0de7d7af37092151524553b9c29a029f81fe7c9f/hiredis-3.3.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:6e2e1024f0a021777740cb7c633a0efb2c4a4bc570f508223a8dcbcf79f99ef9", size = 46043, upload-time = "2026-03-16T15:19:06.928Z" }, - { url = "https://files.pythonhosted.org/packages/10/13/433c4dada704c7f1b1b8261e713483b4cdfa462b1fced1a910ca173c1832/hiredis-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1d68c6980d4690a4550bd3db6c03146f7be68ef5d08d38bb1fb68b3e9c32fe3", size = 41818, upload-time = "2026-03-16T15:19:08.423Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ac/1327de61e27e42ff11d3956690be9fa59899d8112a132a213f1350fe2ee9/hiredis-3.3.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0caf3fc8af0767794b335753781c3fa35f2a3e975c098edbc8f733d35d6a95e4", size = 167062, upload-time = "2026-03-16T15:19:09.601Z" }, - { url = "https://files.pythonhosted.org/packages/fe/bf/b49485c0c4fc088875dc45cb7b86e5e703e0daebbd726f872d4c275fab0e/hiredis-3.3.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81a1669b6631976b1dc9d3d58ed1ab3333e9f52feb91a2a1fb8241101ac3b665", size = 178928, upload-time = "2026-03-16T15:19:10.592Z" }, - { url = "https://files.pythonhosted.org/packages/91/55/3d111ba2843d1d35786508bdfe0ce348fbe54ad3b3553ae70d22cd02a4d0/hiredis-3.3.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8139e9011117822391c5bcfd674c5948fb1e4b8cb9adf6f13d9890859ee3a1a", size = 176734, upload-time = "2026-03-16T15:19:11.769Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c6/b54c15a80c6192c0f7b518b2cbdc0c0b713c02428ba3099cba5dc2023e40/hiredis-3.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:042e57de8a2cae91e3e7c0af32960ea2c5107b2f27f68a740295861e68780a8a", size = 168797, upload-time = "2026-03-16T15:19:12.851Z" }, - { url = "https://files.pythonhosted.org/packages/1e/44/9aa4933c6dcce96ad6d3a598a742cf4bec383563b20a45a90a5d78dcc10a/hiredis-3.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:65f6ac06a9f0c32c254660ec6a9329d81d589e8f5d0a9837a941d5424a6be1ef", size = 163476, upload-time = "2026-03-16T15:19:13.83Z" }, - { url = "https://files.pythonhosted.org/packages/93/f9/810dce091dcf937e5850f2fb03de18d1cd2f4f75d75a0cdc375c333917d9/hiredis-3.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:002fc0201b9af1cc8960e27cdc501ad1f8cdd6dbadb2091c6ddbd4e5ace6cb77", size = 174187, upload-time = "2026-03-16T15:19:15.198Z" }, - { url = "https://files.pythonhosted.org/packages/b7/5c/9ccc16da757d9be131809a9b33022bbe95a6afcdf9dc9694756b8ebd70e6/hiredis-3.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9ebae74ce2b977c2fcb22d6a10aa0acb730022406977b2bcb6ddd6788f5c414a", size = 167144, upload-time = "2026-03-16T15:19:16.352Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bb/0fb0655601cd94db6b2445494528a23915b111d142db6f6d2a7ef741c646/hiredis-3.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8a52b24cd710690c4a7e191c7e300136ad2ecb3c68ffe7e95b598e76de166e5e", size = 164898, upload-time = "2026-03-16T15:19:17.53Z" }, - { url = "https://files.pythonhosted.org/packages/b4/91/f53da281112f469fe8b7eb5999b4c5e5f683b79fee797dbee7e88a697b14/hiredis-3.3.1-cp310-cp310-win32.whl", hash = "sha256:1ebc307a87b099d0877dbd2bdc0bae427258e7ec67f60a951e89027f8dc2568f", size = 20397, upload-time = "2026-03-16T15:19:18.458Z" }, - { url = "https://files.pythonhosted.org/packages/b0/a3/bc6f97c215cedd0c15d9cda91358db492767f5257824cc0c7af6a64f1c38/hiredis-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:62cc62284541bb2a86c898c7d5e8388661cade91c184cb862095ed547e80588f", size = 22331, upload-time = "2026-03-16T15:19:19.277Z" }, - { url = "https://files.pythonhosted.org/packages/8b/71/b8e7da87ff0a270e086670da46732ff8e0af2fb4042afe1486846cf44ea7/hiredis-3.3.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:26f899cde0279e4b7d370716ff80320601c2bd93cdf3e774a42bdd44f65b41f8", size = 81823, upload-time = "2026-03-16T15:19:20.139Z" }, - { url = "https://files.pythonhosted.org/packages/50/e0/8bdafc6251aada93c670eb1893335bb248e10faa784f54de6b9384c7d2cb/hiredis-3.3.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:a2f049c3f3c83e886cd1f53958e2a1ebb369be626bef9e50d8b24d79864f1df6", size = 46043, upload-time = "2026-03-16T15:19:21.292Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e8/48e5eee6dffb2d5659f437231341bfbf00c53d9fdb5d069ea629f1b2fa96/hiredis-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5f316cf2d0558f5027aab19dde7d7e4901c26c21fa95367bc37784e8f547bbf2", size = 41813, upload-time = "2026-03-16T15:19:22.404Z" }, - { url = "https://files.pythonhosted.org/packages/d8/e0/8dcd593db6d0e91cd797fafc565995cd28bd9d7ae85807c820b5e245ab82/hiredis-3.3.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03baa381964b8df356d19ec4e3a6ae656044249a87b0def257fe1e08dbaf6094", size = 167570, upload-time = "2026-03-16T15:19:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/76/e5/e2d75ecc15db51117ebd260fab4059b8a4cbbf74eb89c407c6d437bd6413/hiredis-3.3.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304481241e081bc26f0778b2c2b99f9c43917e4e724a016dcc9439b7ab12c726", size = 179373, upload-time = "2026-03-16T15:19:24.739Z" }, - { url = "https://files.pythonhosted.org/packages/86/9a/4fafde37b86f70125bcd01e8af5e9f448fc99f4116db6d0e9ad214fc688a/hiredis-3.3.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8597c35c9e82f65fd5897c4a2188c65d7daf10607b102960137b23d261cd957b", size = 177501, upload-time = "2026-03-16T15:19:25.982Z" }, - { url = "https://files.pythonhosted.org/packages/a3/73/413a17d6926c015683a608c148862f1dc7e8ad6f5c205b626607be9b9ddf/hiredis-3.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad940dc2db545dc978cb41cb9a683e2ff328f3ef581230b9ca40ff6c3d01d542", size = 169446, upload-time = "2026-03-16T15:19:27.35Z" }, - { url = "https://files.pythonhosted.org/packages/e3/39/aa8e41d5f728dfa99f2236c1176a6b348c7577fd68ca9960d20d251d3b29/hiredis-3.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:156be6a0c736ee145cfe0fb155d0e96cec8d4872cf8b4f76ad6a2ee6ab391d0a", size = 164009, upload-time = "2026-03-16T15:19:28.426Z" }, - { url = "https://files.pythonhosted.org/packages/c4/37/85a609a2cf2b6354749bcef8f488c3298976358601cb4906bbaf2eb53944/hiredis-3.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:583de2f16528e66081cbdfe510d8488c2de73039dc00aada7d22bd49d73a4a94", size = 174623, upload-time = "2026-03-16T15:19:29.466Z" }, - { url = "https://files.pythonhosted.org/packages/a3/5e/75e0a76e4c9021f9914cfa1de8d98cff4acd0a0eb3344d31f43c02ec9375/hiredis-3.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c24c1460486b6b36083252c2db21a814becf8495ccd0e76b7286623e37239b63", size = 167649, upload-time = "2026-03-16T15:19:30.438Z" }, - { url = "https://files.pythonhosted.org/packages/f7/08/1212138ee61e9b72d3f561da60cf6dc15031c10117735938ac258613803f/hiredis-3.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a58a58cef0d911b1717154179a9ff47852249c536ea5966bde4370b6b20638ff", size = 165451, upload-time = "2026-03-16T15:19:31.404Z" }, - { url = "https://files.pythonhosted.org/packages/46/36/cd776ef13b44afbb86c3d63c1a76b09d54cb1b545cce9e26fcd439d69606/hiredis-3.3.1-cp311-cp311-win32.whl", hash = "sha256:e0db44cf81e4d7b94f3776b9f89111f74ed6bbdbfd42a22bc4a5ce0644d3e060", size = 20399, upload-time = "2026-03-16T15:19:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/df/0e/5b2a73bea6d18e7ebda7ed73520854cdc176ba70a945bd541bdeeb3f8caa/hiredis-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:1f7bceb03a1b934872ffe3942eaeed7c7e09096e67b53f095b81f39c7a819113", size = 22336, upload-time = "2026-03-16T15:19:33.238Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/1a7d925d886211948ab9cca44221b1d9dd4d3481d015511e98794e37d369/hiredis-3.3.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:60543f3b068b16a86e99ed96b7fdae71cdc1d8abdfe9b3f82032a555e52ece7e", size = 82023, upload-time = "2026-03-16T15:19:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/13/2f/a6017fe1db47cd63a4aefc0dd21dd4dcb0c4e857bfbcfaa27329745f24a3/hiredis-3.3.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:2611bfaaadc5e8d43fb7967f9bbf1110c8beaa83aee2f2d812c76f11cfb56c6a", size = 46215, upload-time = "2026-03-16T15:19:35.068Z" }, - { url = "https://files.pythonhosted.org/packages/77/4b/35a71d088c6934e162aa81c7e289fa3110a3aca84ab695d88dbd488c74a2/hiredis-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e3754ce60e1b11b0afad9a053481ff184d2ee24bea47099107156d1b84a84aa", size = 41861, upload-time = "2026-03-16T15:19:36.32Z" }, - { url = "https://files.pythonhosted.org/packages/1f/54/904bc723a95926977764fefd6f0d46067579bac38fffc32b806f3f2c05c0/hiredis-3.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e89dabf436ee79b358fd970dcbed6333a36d91db73f27069ca24a02fb138a404", size = 170196, upload-time = "2026-03-16T15:19:37.274Z" }, - { url = "https://files.pythonhosted.org/packages/1d/01/4e840cd4cb53c28578234708b08fb9ec9e41c2880acc0e269a7264e1b3af/hiredis-3.3.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4f7e242eab698ad0be5a4b2ec616fa856569c57455cc67c625fd567726290e5f", size = 181808, upload-time = "2026-03-16T15:19:38.637Z" }, - { url = "https://files.pythonhosted.org/packages/87/0d/fc845f06f8203ab76c401d4d2b97f9fb768e644b053a40f441f7dcc71f2d/hiredis-3.3.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53148a4e21057541b6d8e493b2ea1b500037ddf34433c391970036f3cbce00e3", size = 180577, upload-time = "2026-03-16T15:19:39.749Z" }, - { url = "https://files.pythonhosted.org/packages/52/3a/859afe2620666bf6d58eb977870c47d98af4999d473b50528b323918f3f7/hiredis-3.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25132902d3eff38781e0d54f27a0942ec849e3c07dbdce83c4d92b7e43c8dce", size = 172507, upload-time = "2026-03-16T15:19:40.87Z" }, - { url = "https://files.pythonhosted.org/packages/60/a8/004349708ad8bf0d188d46049f846d3fe2d4a7a8d0d5a6a8ba024017d8b3/hiredis-3.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3fb6573efa15a29c12c0c0f7170b14e7c1347fe4bb39b6a15b779f46015cc929", size = 166339, upload-time = "2026-03-16T15:19:41.912Z" }, - { url = "https://files.pythonhosted.org/packages/c3/fb/bfc6df29381830c99bfd9e97ed3b6d75d9303866a28c23d51ab8c50f63e3/hiredis-3.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:487658e1db83c1ee9fbbac6a43039ea76957767a5987ffb16b590613f9e68297", size = 176766, upload-time = "2026-03-16T15:19:42.981Z" }, - { url = "https://files.pythonhosted.org/packages/53/e7/f54aaad4559a413ec8b1043a89567a5a1f898426e4091b9af5e0f2120371/hiredis-3.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a1d190790ee39b8b7adeeb10fc4090dc4859eb4e75ed27bd8108710eef18f358", size = 170313, upload-time = "2026-03-16T15:19:44.082Z" }, - { url = "https://files.pythonhosted.org/packages/60/51/b80394db4c74d4cba342fa4208f690a2739c16f1125c2a62ba1701b8e2b7/hiredis-3.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a42c7becd4c9ec4ab5769c754eb61112777bdc6e1c1525e2077389e193b5f5aa", size = 167964, upload-time = "2026-03-16T15:19:45.237Z" }, - { url = "https://files.pythonhosted.org/packages/47/ef/5e438d1e058be57cdc1bafc1b1ec8ab43cc890c61447e88f8b878a0e32c3/hiredis-3.3.1-cp312-cp312-win32.whl", hash = "sha256:17ec8b524055a88b80d76c177dbbbe475a25c17c5bf4b67bdbdbd0629bcae838", size = 20532, upload-time = "2026-03-16T15:19:46.233Z" }, - { url = "https://files.pythonhosted.org/packages/e9/c6/39994b9c5646e7bf7d5e92170c07fd5f224ae9f34d95ff202f31845eb94b/hiredis-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:0fac4af8515e6cca74fc701169ae4dc9a71a90e9319c9d21006ec9454b43aa2f", size = 22381, upload-time = "2026-03-16T15:19:47.082Z" }, - { url = "https://files.pythonhosted.org/packages/d8/4b/c7f4d6d6643622f296395269e24b02c69d4ac72822f052b8cae16fa3af03/hiredis-3.3.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:afe3c3863f16704fb5d7c2c6ff56aaf9e054f6d269f7b4c9074c5476178d1aba", size = 82027, upload-time = "2026-03-16T15:19:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/9b/45/198be960a7443d6eb5045751e929480929c0defbca316ce1a47d15187330/hiredis-3.3.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:f19ee7dc1ef8a6497570d91fa4057ba910ad98297a50b8c44ff37589f7c89d17", size = 46220, upload-time = "2026-03-16T15:19:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a4/6ab925177f289830008dbe1488a9858675e2e234f48c9c1653bd4d0eaddc/hiredis-3.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:09f5e510f637f2c72d2a79fb3ad05f7b6211e057e367ca5c4f97bb3d8c9d71f4", size = 41858, upload-time = "2026-03-16T15:19:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c8/a0ddbb9e9c27fcb0022f7b7e93abc75727cb634c6a5273ca5171033dac78/hiredis-3.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b46e96b50dad03495447860510daebd2c96fd44ed25ba8ccb03e9f89eaa9d34", size = 170095, upload-time = "2026-03-16T15:19:51.216Z" }, - { url = "https://files.pythonhosted.org/packages/94/06/618d509cc454912028f71995f3dd6eb54606f0aa8163ff79c5b7ec1f2bda/hiredis-3.3.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b4fe7f38aa8956fcc1cea270e62601e0e11066aff78e384be70fd283d30293b6", size = 181745, upload-time = "2026-03-16T15:19:52.72Z" }, - { url = "https://files.pythonhosted.org/packages/06/14/75b2deb62a61fc75a41ce1a6a781fe239133bbc88fef404d32a148ad152a/hiredis-3.3.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b96da7e365d6488d2a75266a662cbe3cc14b28c23dd9b0c9aa04b5bc5c20192", size = 180465, upload-time = "2026-03-16T15:19:53.847Z" }, - { url = "https://files.pythonhosted.org/packages/7e/8c/8e03dcbfde8e2ca3f880fce06ad0877b3f098ed5fdfb17cf3b821a32323a/hiredis-3.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52d5641027d6731bc7b5e7d126a5158a99784a9f8c6de3d97ca89aca4969e9f8", size = 172419, upload-time = "2026-03-16T15:19:54.959Z" }, - { url = "https://files.pythonhosted.org/packages/03/05/843005d68403a3805309075efc6638360a3ababa6cb4545163bf80c8e7f7/hiredis-3.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eddeb9a153795cf6e615f9f3cef66a1d573ff3b6ee16df2b10d1d1c2f2baeaa8", size = 166398, upload-time = "2026-03-16T15:19:56.36Z" }, - { url = "https://files.pythonhosted.org/packages/f5/23/abe2476244fd792f5108009ec0ae666eaa5b2165ca19f2e86638d8324ac9/hiredis-3.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:011a9071c3df4885cac7f58a2623feac6c8e2ad30e6ba93c55195af05ce61ff5", size = 176844, upload-time = "2026-03-16T15:19:57.462Z" }, - { url = "https://files.pythonhosted.org/packages/c6/47/e1cdccc559b98e548bcff0868c3938d375663418c0adca465895ee1f72e7/hiredis-3.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:264ee7e9cb6c30dc78da4ecf71d74cf14ca122817c665d838eda8b4384bce1b0", size = 170366, upload-time = "2026-03-16T15:19:58.548Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e1/fda8325f51d06877e8e92500b15d4aff3855b4c3c91dbd9636a82e4591f2/hiredis-3.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d1434d0bcc1b3ef048bae53f26456405c08aeed9827e65b24094f5f3a6793f1", size = 168023, upload-time = "2026-03-16T15:19:59.727Z" }, - { url = "https://files.pythonhosted.org/packages/cd/21/2839d1625095989c116470e2b6841bbe1a2a5509585e82a4f3f5cd47f511/hiredis-3.3.1-cp313-cp313-win32.whl", hash = "sha256:f915a34fb742e23d0d61573349aa45d6f74037fde9d58a9f340435eff8d62736", size = 20535, upload-time = "2026-03-16T15:20:00.938Z" }, - { url = "https://files.pythonhosted.org/packages/84/f9/534c2a89b24445a9a9623beb4697fd72b8c8f16286f6f3bda012c7af004a/hiredis-3.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:d8e56e0d1fe607bfff422633f313aec9191c3859ab99d11ff097e3e6e068000c", size = 22383, upload-time = "2026-03-16T15:20:01.865Z" }, - { url = "https://files.pythonhosted.org/packages/03/72/0450d6b449da58120c5497346eb707738f8f67b9e60c28a8ef90133fc81f/hiredis-3.3.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:439f9a5cc8f9519ce208a24cdebfa0440fef26aa682a40ba2c92acb10a53f5e0", size = 82112, upload-time = "2026-03-16T15:20:02.865Z" }, - { url = "https://files.pythonhosted.org/packages/22/c0/0be33a29bcd463e6cbb0282515dd4d0cdfe33c30c7afc6d4d8c460e23266/hiredis-3.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3724f0e58c6ff76fd683429945491de71324ab1bc0ad943a8d68cb0932d24075", size = 46238, upload-time = "2026-03-16T15:20:03.896Z" }, - { url = "https://files.pythonhosted.org/packages/62/f2/f999854bfaf3bcbee0f797f24706c182ecfaca825f6a582f6281a6aa97e0/hiredis-3.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29fe35e3c6fe03204e75c86514f452591957a1e06b05d86e10d795455b71c355", size = 41891, upload-time = "2026-03-16T15:20:04.939Z" }, - { url = "https://files.pythonhosted.org/packages/f2/c8/cd9ab90fec3a301d864d8ab6167aea387add8e2287969d89cbcd45d6b0e0/hiredis-3.3.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d42f3a13290f89191568fc113d95a3d2c8759cdd8c3672f021d8b7436f909e75", size = 170485, upload-time = "2026-03-16T15:20:06.284Z" }, - { url = "https://files.pythonhosted.org/packages/ac/9a/1ddf9ea236a292963146cbaf6722abeb9d503ca47d821267bb8b3b81c4f7/hiredis-3.3.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2afc675b831f7552da41116fffffca4340f387dc03f56d6ec0c7895ab0b59a10", size = 182030, upload-time = "2026-03-16T15:20:07.857Z" }, - { url = "https://files.pythonhosted.org/packages/d4/b8/e070a1dbf8a1bbb8814baa0b00836fbe3f10c7af8e11f942cc739c64e062/hiredis-3.3.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4106201cd052d9eabe3cb7b5a24b0fe37307792bda4fcb3cf6ddd72f697828e8", size = 180543, upload-time = "2026-03-16T15:20:09.096Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bb/b5f4f98e44626e2446cd8a52ce6cb1fc1c99786b6e2db3bf09cea97b90cd/hiredis-3.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8887bf0f31e4b550bd988c8863b527b6587d200653e9375cd91eea2b944b7424", size = 172356, upload-time = "2026-03-16T15:20:10.245Z" }, - { url = "https://files.pythonhosted.org/packages/ef/93/73a77b54ba94e82f76d02563c588d8a062513062675f483a033a43015f2c/hiredis-3.3.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ac7697365dbe45109273b34227fee6826b276ead9a4a007e0877e1d3f0fcf21", size = 166433, upload-time = "2026-03-16T15:20:11.789Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c2/1b2dcbe5dc53a46a8cb05bed67d190a7e30bad2ad1f727ebe154dfeededd/hiredis-3.3.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2b6da6e07359107c653a809b3cff2d9ccaeedbafe33c6f16434aef6f53ce4a2b", size = 177220, upload-time = "2026-03-16T15:20:12.991Z" }, - { url = "https://files.pythonhosted.org/packages/02/09/f4314cf096552568b5ea785ceb60c424771f4d35a76c410ad39d258f74bc/hiredis-3.3.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:ce334915f5d31048f76a42c607bf26687cf045eb1bc852b7340f09729c6a64fc", size = 170475, upload-time = "2026-03-16T15:20:14.519Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2e/3f56e438efc8fc27ed4a3dbad58c0280061466473ec35d8f86c90c841a84/hiredis-3.3.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee11fd431f83d8a5b29d370b9d79a814d3218d30113bdcd44657e9bdf715fc92", size = 167913, upload-time = "2026-03-16T15:20:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/56/34/053e5ee91d6dc478faac661996d1fd4886c5acb7a1b5ac30e7d3c794bb51/hiredis-3.3.1-cp314-cp314-win32.whl", hash = "sha256:e0356561b4a97c83b9ee3de657a41b8d1a1781226853adaf47b550bb988fda6f", size = 21167, upload-time = "2026-03-16T15:20:17.013Z" }, - { url = "https://files.pythonhosted.org/packages/ea/33/06776c641d17881a9031e337e81b3b934c38c2adbb83c85062d6b5f83b72/hiredis-3.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:80aba5f85d6227faee628ae28d1c3b69c661806a0636548ac56c68782606454f", size = 23000, upload-time = "2026-03-16T15:20:17.966Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5a/94f9a505b2ff5376d4a05fb279b69d89bafa7219dd33f6944026e3e56f80/hiredis-3.3.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:907f7b5501a534030738f0f27459a612d2266fd0507b007bb8f3e6de08167920", size = 83039, upload-time = "2026-03-16T15:20:19.316Z" }, - { url = "https://files.pythonhosted.org/packages/93/ae/d3752a8f03a1fca43d402389d2a2d234d3db54c4d1f07f26c1041ca3c5de/hiredis-3.3.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:de94b409f49eb6a588ebdd5872e826caec417cd77c17af0fb94f2128427f1a2a", size = 46703, upload-time = "2026-03-16T15:20:20.401Z" }, - { url = "https://files.pythonhosted.org/packages/9f/76/e32c868a2fa23cd82bacaffd38649d938173244a0e717ec1c0c76874dbdd/hiredis-3.3.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79cd03e7ff550c17758a7520bf437c156d3d4c8bb74214deeafa69cda49c85a4", size = 42379, upload-time = "2026-03-16T15:20:21.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f6/d687d36a74ce6cf448826cf2e8edfc1eb37cc965308f74eb696aa97c69df/hiredis-3.3.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ffa7ba2e2da1f806f3181b9730b3e87ba9dbfec884806725d4584055ba3faa6", size = 180311, upload-time = "2026-03-16T15:20:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/db/ac/f520dc0066a62a15aa920c7dd0a2028c213f4862d5f901409ae92ee5d785/hiredis-3.3.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ee37fe8cf081b72dea72f96a0ee604f492ec02252eb77dc26ff6eec3f997b580", size = 190488, upload-time = "2026-03-16T15:20:24.357Z" }, - { url = "https://files.pythonhosted.org/packages/4d/f5/ae10fff82d0f291e90c41bf10a5d6543a96aae00cccede01bf2b6f7e178d/hiredis-3.3.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9bfdeff778d3f7ff449ca5922ab773899e7d31e26a576028b06a5e9cf0ed8c34", size = 189210, upload-time = "2026-03-16T15:20:25.51Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8f/5be4344e542aa8d349a03d05486c59d9ca26f69c749d11e114bf34b84d50/hiredis-3.3.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:027ce4fabfeff5af5b9869d5524770877f9061d118bc36b85703ae3faf5aad8e", size = 180971, upload-time = "2026-03-16T15:20:26.631Z" }, - { url = "https://files.pythonhosted.org/packages/41/a2/29e230226ec2a31f13f8a832fbafe366e263f3b090553ebe49bb4581a7bd/hiredis-3.3.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dcea8c3f53674ae68e44b12e853b844a1d315250ca6677b11ec0c06aff85e86c", size = 175314, upload-time = "2026-03-16T15:20:27.848Z" }, - { url = "https://files.pythonhosted.org/packages/89/2e/bf241707ad86b9f3ebfbc7ab89e19d5ec243ff92ca77644a383622e8740b/hiredis-3.3.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0b5ff2f643f4b452b0597b7fe6aa35d398cb31d8806801acfafb1558610ea2aa", size = 185652, upload-time = "2026-03-16T15:20:29.364Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c1/b39170d8bcccd01febd45af4ac6b43ff38e134a868e2ec167a82a036fb35/hiredis-3.3.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3586c8a5f56d34b9dddaaa9e76905f31933cac267251006adf86ec0eef7d0400", size = 179033, upload-time = "2026-03-16T15:20:30.549Z" }, - { url = "https://files.pythonhosted.org/packages/b7/3a/4fe39a169115434f911abff08ff485b9b6201c168500e112b3f6a8110c0a/hiredis-3.3.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a110d19881ca78a88583d3b07231e7c6864864f5f1f3491b638863ea45fa8708", size = 176126, upload-time = "2026-03-16T15:20:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/44/99/c1d0b0bc4f9e9150e24beb0dca2e186e32d5e749d0022e0d26453749ed51/hiredis-3.3.1-cp314-cp314t-win32.whl", hash = "sha256:98fd5b39410e9d69e10e90d0330e35650becaa5dd2548f509b9598f1f3c6124d", size = 22028, upload-time = "2026-03-16T15:20:33.33Z" }, - { url = "https://files.pythonhosted.org/packages/35/d6/191e6741addc97bcf5e755661f8c82f0fd0aa35f07ece56e858da689b57e/hiredis-3.3.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ab1f646ff531d70bfd25f01e60708dfa3d105eb458b7dedd9fe9a443039fd809", size = 23811, upload-time = "2026-03-16T15:20:34.292Z" }, -] - [[package]] name = "httpcore" version = "1.0.9" @@ -843,6 +1117,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + [[package]] name = "idna" version = "3.15" @@ -852,6 +1135,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, ] +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + [[package]] name = "inflect" version = "7.5.0" @@ -895,6 +1190,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jsonschema" +version = "4.17.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "attrs", version = "21.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pyrsistent", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/3d/ca032d5ac064dff543aa13c984737795ac81abc9fb130cd2fcff17cfabc7/jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", size = 297785, upload-time = "2022-11-29T20:37:47.453Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/97/c698bd9350f307daad79dd740806e1a59becd693bd11443a0f531e3229b3/jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6", size = 90379, upload-time = "2022-11-29T20:37:45.842Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.25.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +dependencies = [ + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "jsonschema-specifications", marker = "python_full_version >= '3.14'" }, + { name = "referencing", marker = "python_full_version >= '3.14'" }, + { name = "rpds-py", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + [[package]] name = "librt" version = "0.11.0" @@ -992,6 +1335,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -1077,6 +1432,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] +[[package]] +name = "mcp" +version = "1.23.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "python_full_version >= '3.14'" }, + { name = "httpx", marker = "python_full_version >= '3.14'" }, + { name = "httpx-sse", marker = "python_full_version >= '3.14'" }, + { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "pydantic", marker = "python_full_version >= '3.14'" }, + { name = "pydantic-settings", marker = "python_full_version >= '3.14'" }, + { name = "pyjwt", extra = ["crypto"], marker = "python_full_version >= '3.14'" }, + { name = "python-multipart", marker = "python_full_version >= '3.14'" }, + { name = "pywin32", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, + { name = "sse-starlette", marker = "python_full_version >= '3.14'" }, + { name = "starlette", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, + { name = "typing-inspection", marker = "python_full_version >= '3.14'" }, + { name = "uvicorn", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/a4/d06a303f45997e266f2c228081abe299bbcba216cb806128e2e49095d25f/mcp-1.23.3.tar.gz", hash = "sha256:b3b0da2cc949950ce1259c7bfc1b081905a51916fcd7c8182125b85e70825201", size = 600697, upload-time = "2025-12-09T16:04:37.351Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/c6/13c1a26b47b3f3a3b480783001ada4268917c9f42d78a079c336da2e75e5/mcp-1.23.3-py3-none-any.whl", hash = "sha256:32768af4b46a1b4f7df34e2bfdf5c6011e7b63d7f1b0e321d0fdef4cd6082031", size = 231570, upload-time = "2025-12-09T16:04:35.56Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "more-itertools" version = "11.0.2" @@ -1095,7 +1484,7 @@ dependencies = [ { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } @@ -1155,23 +1544,187 @@ wheels = [ ] [[package]] -name = "packaging" -version = "26.2" +name = "opentelemetry-api" +version = "1.37.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/04/05040d7ce33a907a2a02257e601992f0cdf11c73b33f13c4492bf6c3d6d5/opentelemetry_api-1.37.0.tar.gz", hash = "sha256:540735b120355bd5112738ea53621f8d5edb35ebcd6fe21ada3ab1c61d1cd9a7", size = 64923, upload-time = "2025-09-11T10:29:01.662Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, + { url = "https://files.pythonhosted.org/packages/91/48/28ed9e55dcf2f453128df738210a980e09f4e468a456fa3c763dbc8be70a/opentelemetry_api-1.37.0-py3-none-any.whl", hash = "sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47", size = 65732, upload-time = "2025-09-11T10:28:41.826Z" }, ] [[package]] -name = "pathspec" -version = "1.1.1" +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/6c/10018cbcc1e6fff23aac67d7fd977c3d692dbe5f9ef9bb4db5c1268726cc/opentelemetry_exporter_otlp_proto_common-1.37.0.tar.gz", hash = "sha256:c87a1bdd9f41fdc408d9cc9367bb53f8d2602829659f2b90be9f9d79d0bfe62c", size = 20430, upload-time = "2025-09-11T10:29:03.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/13/b4ef09837409a777f3c0af2a5b4ba9b7af34872bc43609dda0c209e4060d/opentelemetry_exporter_otlp_proto_common-1.37.0-py3-none-any.whl", hash = "sha256:53038428449c559b0c564b8d718df3314da387109c4d36bd1b94c9a641b0292e", size = 18359, upload-time = "2025-09-11T10:28:44.939Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-exporter-otlp-proto-common", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-proto", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-sdk", marker = "python_full_version >= '3.14'" }, + { name = "requests", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/e3/6e320aeb24f951449e73867e53c55542bebbaf24faeee7623ef677d66736/opentelemetry_exporter_otlp_proto_http-1.37.0.tar.gz", hash = "sha256:e52e8600f1720d6de298419a802108a8f5afa63c96809ff83becb03f874e44ac", size = 17281, upload-time = "2025-09-11T10:29:04.844Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/e9/70d74a664d83976556cec395d6bfedd9b85ec1498b778367d5f93e373397/opentelemetry_exporter_otlp_proto_http-1.37.0-py3-none-any.whl", hash = "sha256:54c42b39945a6cc9d9a2a33decb876eabb9547e0dcb49df090122773447f1aef", size = 19576, upload-time = "2025-09-11T10:28:46.726Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.58b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-semantic-conventions", marker = "python_full_version >= '3.14'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "wrapt", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/36/7c307d9be8ce4ee7beb86d7f1d31027f2a6a89228240405a858d6e4d64f9/opentelemetry_instrumentation-0.58b0.tar.gz", hash = "sha256:df640f3ac715a3e05af145c18f527f4422c6ab6c467e40bd24d2ad75a00cb705", size = 31549, upload-time = "2025-09-11T11:42:14.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/db/5ff1cd6c5ca1d12ecf1b73be16fbb2a8af2114ee46d4b0e6d4b23f4f4db7/opentelemetry_instrumentation-0.58b0-py3-none-any.whl", hash = "sha256:50f97ac03100676c9f7fc28197f8240c7290ca1baa12da8bfbb9a1de4f34cc45", size = 33019, upload-time = "2025-09-11T11:41:00.624Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-requests" +version = "0.58b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-instrumentation", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-semantic-conventions", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-util-http", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/42/83ee32de763b919779aaa595b60c5a7b9c0a4b33952bbe432c5f6a783085/opentelemetry_instrumentation_requests-0.58b0.tar.gz", hash = "sha256:ae9495e6ff64e27bdb839fce91dbb4be56e325139828e8005f875baf41951a2e", size = 15188, upload-time = "2025-09-11T11:42:51.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/4d/f3476b28ea167d1762134352d01ae9693940a42c78994d9f1b32a4477816/opentelemetry_instrumentation_requests-0.58b0-py3-none-any.whl", hash = "sha256:672a0be0bb5b52bea0c11820b35e27edcf4cd22d34abe4afc59a92a80519f8a8", size = 12966, upload-time = "2025-09-11T11:41:52.67Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-threading" +version = "0.58b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-instrumentation", marker = "python_full_version >= '3.14'" }, + { name = "wrapt", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/a9/3888cb0470e6eb48ea17b6802275ae71df411edd6382b9a8e8f391936fda/opentelemetry_instrumentation_threading-0.58b0.tar.gz", hash = "sha256:f68c61f77841f9ff6270176f4d496c10addbceacd782af434d705f83e4504862", size = 8770, upload-time = "2025-09-11T11:42:56.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/54/add1076cb37980e617723a96e29c84006983e8ad6fc589dde7f69ddc57d4/opentelemetry_instrumentation_threading-0.58b0-py3-none-any.whl", hash = "sha256:eacc072881006aceb5b9b6831bcdce718c67ef6f31ac0b32bd6a23a94d979b4a", size = 9312, upload-time = "2025-09-11T11:41:58.603Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/ea/a75f36b463a36f3c5a10c0b5292c58b31dbdde74f6f905d3d0ab2313987b/opentelemetry_proto-1.37.0.tar.gz", hash = "sha256:30f5c494faf66f77faeaefa35ed4443c5edb3b0aa46dad073ed7210e1a789538", size = 46151, upload-time = "2025-09-11T10:29:11.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/25/f89ea66c59bd7687e218361826c969443c4fa15dfe89733f3bf1e2a9e971/opentelemetry_proto-1.37.0-py3-none-any.whl", hash = "sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2", size = 72534, upload-time = "2025-09-11T10:28:56.831Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-semantic-conventions", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/62/2e0ca80d7fe94f0b193135375da92c640d15fe81f636658d2acf373086bc/opentelemetry_sdk-1.37.0.tar.gz", hash = "sha256:cc8e089c10953ded765b5ab5669b198bbe0af1b3f89f1007d19acd32dc46dda5", size = 170404, upload-time = "2025-09-11T10:29:11.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/62/9f4ad6a54126fb00f7ed4bb5034964c6e4f00fcd5a905e115bd22707e20d/opentelemetry_sdk-1.37.0-py3-none-any.whl", hash = "sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c", size = 131941, upload-time = "2025-09-11T10:28:57.83Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.58b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/1b/90701d91e6300d9f2fb352153fb1721ed99ed1f6ea14fa992c756016e63a/opentelemetry_semantic_conventions-0.58b0.tar.gz", hash = "sha256:6bd46f51264279c433755767bb44ad00f1c9e2367e1b42af563372c5a6fa0c25", size = 129867, upload-time = "2025-09-11T10:29:12.597Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954, upload-time = "2025-09-11T10:28:59.218Z" }, +] + +[[package]] +name = "opentelemetry-util-http" +version = "0.58b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/5f/02f31530faf50ef8a41ab34901c05cbbf8e9d76963ba2fb852b0b4065f4e/opentelemetry_util_http-0.58b0.tar.gz", hash = "sha256:de0154896c3472c6599311c83e0ecee856c4da1b17808d39fdc5cce5312e4d89", size = 9411, upload-time = "2025-09-11T11:43:05.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/a3/0a1430c42c6d34d8372a16c104e7408028f0c30270d8f3eb6cccf2e82934/opentelemetry_util_http-0.58b0-py3-none-any.whl", hash = "sha256:6c6b86762ed43025fbd593dc5f700ba0aa3e09711aedc36fd48a13b23d8cb1e7", size = 7652, upload-time = "2025-09-11T11:42:09.682Z" }, +] + +[[package]] +name = "packaging" +version = "21.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "pyparsing", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/9e/d1a7217f69310c1db8fdf8ab396229f55a699ce34a203691794c5d1cad0c/packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", size = 84848, upload-time = "2021-11-18T00:39:13.586Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/8e/8de486cbd03baba4deef4142bd643a3e7bbe954a784dc1bb17142572d127/packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522", size = 40750, upload-time = "2021-11-18T00:39:10.932Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] +[[package]] +name = "peewee" +version = "3.19.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/b0/79462b42e89764998756e0557f2b58a15610a5b4512fbbcccae58fba7237/peewee-3.19.0.tar.gz", hash = "sha256:f88292a6f0d7b906cb26bca9c8599b8f4d8920ebd36124400d0cbaaaf915511f", size = 974035, upload-time = "2026-01-07T17:24:59.597Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/41/19c65578ef9a54b3083253c68a607f099642747168fe00f3a2bceb7c3a34/peewee-3.19.0-py3-none-any.whl", hash = "sha256:de220b94766e6008c466e00ce4ba5299b9a832117d9eb36d45d0062f3cfd7417", size = 411885, upload-time = "2026-01-07T17:24:58.33Z" }, +] + [[package]] name = "platformdirs" version = "4.9.6" @@ -1204,64 +1757,19 @@ wheels = [ ] [[package]] -name = "prodik" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "alembic" }, - { name = "argon2-cffi" }, - { name = "asyncpg" }, - { name = "cryptography" }, - { name = "dishka" }, - { name = "fastapi" }, - { name = "pydantic", extra = ["email"] }, - { name = "pyjwt" }, - { name = "redis", extra = ["hiredis"] }, - { name = "sqlalchemy" }, - { name = "structlog" }, - { name = "uvicorn" }, -] - -[package.optional-dependencies] -dev = [ - { name = "datamodel-code-generator" }, - { name = "dirty-equals" }, - { name = "httpx" }, - { name = "mypy" }, - { name = "polyfactory" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-xdist" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "alembic", specifier = "==1.18.4" }, - { name = "argon2-cffi", specifier = "==25.1.0" }, - { name = "asyncpg", specifier = "==0.31.0" }, - { name = "cryptography", specifier = "==46.0.5" }, - { name = "datamodel-code-generator", marker = "extra == 'dev'", specifier = "==0.54.0" }, - { name = "dirty-equals", marker = "extra == 'dev'", specifier = "==0.11" }, - { name = "dishka", specifier = "==1.7.2" }, - { name = "fastapi", specifier = "==0.128.0" }, - { name = "httpx", marker = "extra == 'dev'", specifier = "==0.28.1" }, - { name = "mypy", marker = "extra == 'dev'" }, - { name = "polyfactory", marker = "extra == 'dev'", specifier = "==3.3.0" }, - { name = "pydantic", extras = ["email"], specifier = "==2.13.2" }, - { name = "pyjwt", specifier = "==2.11.0" }, - { name = "pytest", marker = "extra == 'dev'" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = "==1.3.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.1.0" }, - { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.8.0" }, - { name = "redis", extras = ["hiredis"], specifier = "==7.4.0" }, - { name = "ruff", marker = "extra == 'dev'" }, - { name = "sqlalchemy", specifier = "==2.0.46" }, - { name = "structlog", specifier = "==25.5.0" }, - { name = "uvicorn", specifier = "==0.40.0" }, +name = "protobuf" +version = "6.33.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, ] -provides-extras = ["dev"] [[package]] name = "pycparser" @@ -1408,6 +1916,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/f3/0a7087e5f861d66ca64ce927230b397cc264c87b712156e6a93b26a459c8/pydantic_core-2.46.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:154dbfdfb11b8cbd8ff4d00d0b81e3d19f4cb4bedd5aa9f091060ba071474c6a", size = 2192159, upload-time = "2026-04-17T09:11:20.123Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic", marker = "python_full_version >= '3.14'" }, + { name = "python-dotenv", marker = "python_full_version >= '3.14'" }, + { name = "typing-inspection", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -1426,18 +1948,83 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, ] +[package.optional-dependencies] +crypto = [ + { name = "cryptography", marker = "python_full_version >= '3.14'" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyrsistent" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/3a/5031723c09068e9c8c2f0bc25c3a9245f2b1d1aea8396c787a408f2b95ca/pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4", size = 103642, upload-time = "2023-10-25T21:06:56.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/19/c343b14061907b629b765444b6436b160e2bd4184d17d4804bbe6381f6be/pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce", size = 83416, upload-time = "2023-10-25T21:06:04.579Z" }, + { url = "https://files.pythonhosted.org/packages/9f/4f/8342079ea331031ef9ed57edd312a9ad283bcc8adfaf268931ae356a09a6/pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f", size = 118021, upload-time = "2023-10-25T21:06:06.953Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b7/64a125c488243965b7c5118352e47c6f89df95b4ac306d31cee409153d57/pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34", size = 117747, upload-time = "2023-10-25T21:06:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/43c67bd5f80df9e7583042398d12113263ec57f27c0607abe9d78395d18f/pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b", size = 114524, upload-time = "2023-10-25T21:06:10.728Z" }, + { url = "https://files.pythonhosted.org/packages/8a/98/b382a87e89ca839106d874f7bf78d226b3eedb26735eb6f751f1a3375f21/pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f", size = 60780, upload-time = "2023-10-25T21:06:12.14Z" }, + { url = "https://files.pythonhosted.org/packages/37/8a/23e2193f7adea6901262e3cf39c7fe18ac0c446176c0ff0e19aeb2e9681e/pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7", size = 63310, upload-time = "2023-10-25T21:06:13.598Z" }, + { url = "https://files.pythonhosted.org/packages/df/63/7544dc7d0953294882a5c587fb1b10a26e0c23d9b92281a14c2514bac1f7/pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958", size = 83481, upload-time = "2023-10-25T21:06:15.238Z" }, + { url = "https://files.pythonhosted.org/packages/ae/a0/49249bc14d71b1bf2ffe89703acfa86f2017c25cfdabcaea532b8c8a5810/pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8", size = 120222, upload-time = "2023-10-25T21:06:17.144Z" }, + { url = "https://files.pythonhosted.org/packages/a1/94/9808e8c9271424120289b9028a657da336ad7e43da0647f62e4f6011d19b/pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a", size = 120002, upload-time = "2023-10-25T21:06:18.727Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f6/9ecfb78b2fc8e2540546db0fe19df1fae0f56664a5958c21ff8861b0f8da/pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224", size = 116850, upload-time = "2023-10-25T21:06:20.424Z" }, + { url = "https://files.pythonhosted.org/packages/83/c8/e6d28bc27a0719f8eaae660357df9757d6e9ca9be2691595721de9e8adfc/pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656", size = 60775, upload-time = "2023-10-25T21:06:21.815Z" }, + { url = "https://files.pythonhosted.org/packages/98/87/c6ef52ff30388f357922d08de012abdd3dc61e09311d88967bdae23ab657/pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee", size = 63306, upload-time = "2023-10-25T21:06:22.874Z" }, + { url = "https://files.pythonhosted.org/packages/15/ee/ff2ed52032ac1ce2e7ba19e79bd5b05d152ebfb77956cf08fcd6e8d760ea/pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e", size = 83537, upload-time = "2023-10-25T21:06:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/f1/338d0050b24c3132bcfc79b68c3a5f54bce3d213ecef74d37e988b971d8a/pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e", size = 122615, upload-time = "2023-10-25T21:06:25.815Z" }, + { url = "https://files.pythonhosted.org/packages/07/3a/e56d6431b713518094fae6ff833a04a6f49ad0fbe25fb7c0dc7408e19d20/pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3", size = 122335, upload-time = "2023-10-25T21:06:28.631Z" }, + { url = "https://files.pythonhosted.org/packages/4a/bb/5f40a4d5e985a43b43f607250e766cdec28904682c3505eb0bd343a4b7db/pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d", size = 118510, upload-time = "2023-10-25T21:06:30.718Z" }, + { url = "https://files.pythonhosted.org/packages/1c/13/e6a22f40f5800af116c02c28e29f15c06aa41cb2036f6a64ab124647f28b/pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174", size = 60865, upload-time = "2023-10-25T21:06:32.742Z" }, + { url = "https://files.pythonhosted.org/packages/75/ef/2fa3b55023ec07c22682c957808f9a41836da4cd006b5f55ec76bf0fbfa6/pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d", size = 63239, upload-time = "2023-10-25T21:06:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/23/88/0acd180010aaed4987c85700b7cc17f9505f3edb4e5873e4dc67f613e338/pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b", size = 58106, upload-time = "2023-10-25T21:06:54.387Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.14' and sys_platform == 'win32'" }, + { name = "exceptiongroup", version = "1.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "iniconfig", marker = "python_full_version < '3.14'" }, + { name = "packaging", version = "21.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pluggy", marker = "python_full_version < '3.14'" }, + { name = "pygments", marker = "python_full_version < '3.14'" }, + { name = "tomli", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + [[package]] name = "pytest" version = "9.0.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "colorama", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, + { name = "iniconfig", marker = "python_full_version >= '3.14'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "pluggy", marker = "python_full_version >= '3.14'" }, + { name = "pygments", marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ @@ -1450,7 +2037,8 @@ version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, - { name = "pytest" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } @@ -1465,7 +2053,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, - { name = "pytest" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ @@ -1478,13 +2067,44 @@ version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, - { name = "pytest" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-lsp-jsonrpc" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ujson", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/45/1c2a272950679af529f7360af6ee567ef266f282e451be926329e8d50d84/python-lsp-jsonrpc-1.0.0.tar.gz", hash = "sha256:7bec170733db628d3506ea3a5288ff76aa33c70215ed223abdb0d95e957660bd", size = 10011, upload-time = "2021-04-14T21:19:20.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/ee/754bfd5f6bfe7162c10d3ecb0aeef6f882f91d3231596c83f761a75efd0b/python_lsp_jsonrpc-1.0.0-py3-none-any.whl", hash = "sha256:079b143be64b0a378bdb21dff5e28a8c1393fe7e8a654ef068322d754e545fc7", size = 8507, upload-time = "2021-04-14T21:19:18.698Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + [[package]] name = "pytokens" version = "0.4.1" @@ -1524,6 +2144,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, ] +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -1589,20 +2231,251 @@ wheels = [ ] [[package]] -name = "redis" -version = "7.4.0" +name = "referencing" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "rpds-py", marker = "python_full_version >= '3.14'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] -[package.optional-dependencies] -hiredis = [ - { name = "hiredis" }, +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174, upload-time = "2026-06-30T07:14:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513, upload-time = "2026-06-30T07:14:56.515Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783, upload-time = "2026-06-30T07:14:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316, upload-time = "2026-06-30T07:14:59.077Z" }, + { url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423, upload-time = "2026-06-30T07:15:00.488Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077, upload-time = "2026-06-30T07:15:01.911Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315, upload-time = "2026-06-30T07:15:03.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502, upload-time = "2026-06-30T07:15:04.627Z" }, + { url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673, upload-time = "2026-06-30T07:15:05.856Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964, upload-time = "2026-06-30T07:15:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446, upload-time = "2026-06-30T07:15:08.531Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975, upload-time = "2026-06-30T07:15:09.97Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453, upload-time = "2026-06-30T07:15:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219, upload-time = "2026-06-30T07:15:12.476Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137, upload-time = "2026-06-30T07:15:13.631Z" }, + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791, upload-time = "2026-06-30T07:17:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842, upload-time = "2026-06-30T07:17:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094, upload-time = "2026-06-30T07:17:36.514Z" }, + { url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662, upload-time = "2026-06-30T07:17:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896, upload-time = "2026-06-30T07:17:39.689Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545, upload-time = "2026-06-30T07:17:41.336Z" }, + { url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059, upload-time = "2026-06-30T07:17:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235, upload-time = "2026-06-30T07:17:44.637Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008, upload-time = "2026-06-30T07:17:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118, upload-time = "2026-06-30T07:17:47.759Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138, upload-time = "2026-06-30T07:17:49.355Z" }, + { url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486, upload-time = "2026-06-30T07:17:51.141Z" }, +] + +[[package]] +name = "ruamel-yaml" +version = "0.17.40" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/d6/eb2833ccba5ea36f8f4de4bcfa0d1a91eb618f832d430b70e3086821f251/ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d", size = 137672, upload-time = "2023-10-20T12:53:56.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/79/5e2cffa1c77432f11cd93a5351f30732c997a239d3a3090856a72d6d8ba7/ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c", size = 113666, upload-time = "2023-10-20T12:53:52.628Z" }, +] + +[[package]] +name = "ruamel-yaml" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, +] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/e9/39ec4d4b3f91188fad1842748f67d4e749c77c37e353c4e545052ee8e893/ruamel.yaml.clib-0.2.14.tar.gz", hash = "sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e", size = 225394, upload-time = "2025-09-22T19:51:23.753Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/56/35a0a752415ae01992c68f5a6513bdef0e1b6fbdb60d7619342ce12346a0/ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4", size = 269216, upload-time = "2025-09-23T14:24:09.742Z" }, + { url = "https://files.pythonhosted.org/packages/98/6a/9a68184ab93619f4607ff1675e4ef01e8accfcbff0d482f4ca44c10d8eab/ruamel.yaml.clib-0.2.14-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451", size = 137092, upload-time = "2025-09-22T19:50:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/2b/3f/cfed5f088628128a9ec66f46794fd4d165642155c7b78c26d83b16c6bf7b/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543", size = 633768, upload-time = "2025-09-22T19:50:31.228Z" }, + { url = "https://files.pythonhosted.org/packages/3a/d5/5ce2cc156c1da48160171968d91f066d305840fbf930ee955a509d025a44/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8", size = 721253, upload-time = "2025-09-22T19:50:28.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/71/d0b56bc902b38ebe4be8e270f730f929eec4edaf8a0fa7028f4ef64fa950/ruamel.yaml.clib-0.2.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8", size = 683823, upload-time = "2025-09-22T19:50:29.993Z" }, + { url = "https://files.pythonhosted.org/packages/4b/db/1f37449dd89c540218598316ccafc1a0aed60215e72efa315c5367cfd015/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02", size = 690370, upload-time = "2025-09-23T18:42:46.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/53/c498b30f35efcd9f47cb084d7ad9374f2b907470f73913dec6396b81397d/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4", size = 703578, upload-time = "2025-09-22T19:50:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/34/79/492cfad9baed68914840c39e5f3c1cc251f51a897ddb3f532601215cbb12/ruamel.yaml.clib-0.2.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31", size = 722544, upload-time = "2025-09-22T19:50:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f5/479ebfd5ba396e209ade90f7282d84b90c57b3e07be8dc6fcd02a6df7ffc/ruamel.yaml.clib-0.2.14-cp310-cp310-win32.whl", hash = "sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182", size = 100375, upload-time = "2025-09-22T19:50:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/57/31/a044520fdb3bd409889f67f1efebda0658033c7ab3f390cee37531cc9a9e/ruamel.yaml.clib-0.2.14-cp310-cp310-win_amd64.whl", hash = "sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8", size = 118129, upload-time = "2025-09-22T19:50:35.545Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/3c51e9578b8c36fcc4bdd271a1a5bb65963a74a4b6ad1a989768a22f6c2a/ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e", size = 270207, upload-time = "2025-09-23T14:24:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/4a/16/cb02815bc2ae9c66760c0c061d23c7358f9ba51dae95ac85247662b7fbe2/ruamel.yaml.clib-0.2.14-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d", size = 137780, upload-time = "2025-09-22T19:50:37.734Z" }, + { url = "https://files.pythonhosted.org/packages/31/c6/fc687cd1b93bff8e40861eea46d6dc1a6a778d9a085684e4045ff26a8e40/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9", size = 641590, upload-time = "2025-09-22T19:50:41.978Z" }, + { url = "https://files.pythonhosted.org/packages/45/5d/65a2bc08b709b08576b3f307bf63951ee68a8e047cbbda6f1c9864ecf9a7/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70", size = 738090, upload-time = "2025-09-22T19:50:39.152Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d0/a70a03614d9a6788a3661ab1538879ed2aae4e84d861f101243116308a37/ruamel.yaml.clib-0.2.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98", size = 700744, upload-time = "2025-09-22T19:50:40.811Z" }, + { url = "https://files.pythonhosted.org/packages/77/30/c93fa457611f79946d5cb6cc97493ca5425f3f21891d7b1f9b44eaa1b38e/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee", size = 742321, upload-time = "2025-09-23T18:42:48.916Z" }, + { url = "https://files.pythonhosted.org/packages/40/85/e2c54ad637117cd13244a4649946eaa00f32edcb882d1f92df90e079ab00/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d", size = 743805, upload-time = "2025-09-22T19:50:43.58Z" }, + { url = "https://files.pythonhosted.org/packages/81/50/f899072c38877d8ef5382e0b3d47f8c4346226c1f52d6945d6f64fec6a2f/ruamel.yaml.clib-0.2.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c", size = 769529, upload-time = "2025-09-22T19:50:45.707Z" }, + { url = "https://files.pythonhosted.org/packages/99/7c/96d4b5075e30c65ea2064e40c2d657c7c235d7b6ef18751cf89a935b9041/ruamel.yaml.clib-0.2.14-cp311-cp311-win32.whl", hash = "sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a", size = 100256, upload-time = "2025-09-22T19:50:48.26Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8c/73ee2babd04e8bfcf1fd5c20aa553d18bf0ebc24b592b4f831d12ae46cc0/ruamel.yaml.clib-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1", size = 118234, upload-time = "2025-09-22T19:50:47.019Z" }, + { url = "https://files.pythonhosted.org/packages/b4/42/ccfb34a25289afbbc42017e4d3d4288e61d35b2e00cfc6b92974a6a1f94b/ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27", size = 271775, upload-time = "2025-09-23T14:24:12.771Z" }, + { url = "https://files.pythonhosted.org/packages/82/73/e628a92e80197ff6a79ab81ec3fa00d4cc082d58ab78d3337b7ba7043301/ruamel.yaml.clib-0.2.14-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052", size = 138842, upload-time = "2025-09-22T19:50:49.156Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c5/346c7094344a60419764b4b1334d9e0285031c961176ff88ffb652405b0c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a", size = 647404, upload-time = "2025-09-22T19:50:52.921Z" }, + { url = "https://files.pythonhosted.org/packages/df/99/65080c863eb06d4498de3d6c86f3e90595e02e159fd8529f1565f56cfe2c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29", size = 753141, upload-time = "2025-09-22T19:50:50.294Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e3/0de85f3e3333f8e29e4b10244374a202a87665d1131798946ee22cf05c7c/ruamel.yaml.clib-0.2.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4", size = 703477, upload-time = "2025-09-22T19:50:51.508Z" }, + { url = "https://files.pythonhosted.org/packages/d9/25/0d2f09d8833c7fd77ab8efeff213093c16856479a9d293180a0d89f6bed9/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9", size = 741157, upload-time = "2025-09-23T18:42:50.408Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8c/959f10c2e2153cbdab834c46e6954b6dd9e3b109c8f8c0a3cf1618310985/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259", size = 745859, upload-time = "2025-09-22T19:50:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6b/e580a7c18b485e1a5f30a32cda96b20364b0ba649d9d2baaf72f8bd21f83/ruamel.yaml.clib-0.2.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023", size = 770200, upload-time = "2025-09-22T19:50:55.718Z" }, + { url = "https://files.pythonhosted.org/packages/ef/44/3455eebc761dc8e8fdced90f2b0a3fa61e32ba38b50de4130e2d57db0f21/ruamel.yaml.clib-0.2.14-cp312-cp312-win32.whl", hash = "sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54", size = 98829, upload-time = "2025-09-22T19:50:58.895Z" }, + { url = "https://files.pythonhosted.org/packages/76/ab/5121f7f3b651db93de546f8c982c241397aad0a4765d793aca1dac5eadee/ruamel.yaml.clib-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68", size = 115570, upload-time = "2025-09-22T19:50:57.981Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ae/e3811f05415594025e96000349d3400978adaed88d8f98d494352d9761ee/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32", size = 269205, upload-time = "2025-09-23T14:24:15.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/06/7d51f4688d6d72bb72fa74254e1593c4f5ebd0036be5b41fe39315b275e9/ruamel.yaml.clib-0.2.14-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85", size = 137417, upload-time = "2025-09-22T19:50:59.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/08/b4499234a420ef42960eeb05585df5cc7eb25ccb8c980490b079e6367050/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e", size = 642558, upload-time = "2025-09-22T19:51:03.388Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ba/1975a27dedf1c4c33306ee67c948121be8710b19387aada29e2f139c43ee/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb", size = 744087, upload-time = "2025-09-22T19:51:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/8a19a13d27f3bd09fa18813add8380a29115a47b553845f08802959acbce/ruamel.yaml.clib-0.2.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d", size = 699709, upload-time = "2025-09-22T19:51:02.075Z" }, + { url = "https://files.pythonhosted.org/packages/19/ee/8d6146a079ad21e534b5083c9ee4a4c8bec42f79cf87594b60978286b39a/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59", size = 708926, upload-time = "2025-09-23T18:42:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/426b714abdc222392e68f3b8ad323930d05a214a27c7e7a0f06c69126401/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca", size = 740202, upload-time = "2025-09-22T19:51:04.673Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ac/3c5c2b27a183f4fda8a57c82211721c016bcb689a4a175865f7646db9f94/ruamel.yaml.clib-0.2.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6", size = 765196, upload-time = "2025-09-22T19:51:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/06f56a71fd55021c993ed6e848c9b2e5e9cfce180a42179f0ddd28253f7c/ruamel.yaml.clib-0.2.14-cp313-cp313-win32.whl", hash = "sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2", size = 98635, upload-time = "2025-09-22T19:51:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/51/79/76aba16a1689b50528224b182f71097ece338e7a4ab55e84c2e73443b78a/ruamel.yaml.clib-0.2.14-cp313-cp313-win_amd64.whl", hash = "sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78", size = 115238, upload-time = "2025-09-22T19:51:07.081Z" }, + { url = "https://files.pythonhosted.org/packages/21/e2/a59ff65c26aaf21a24eb38df777cb9af5d87ba8fc8107c163c2da9d1e85e/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f", size = 271441, upload-time = "2025-09-23T14:24:16.498Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/150fdb96b8fab27fe08d8a59fe67554568727981806e6bc2677a16081ec7/ruamel_yaml_clib-0.2.14-cp314-cp314-win32.whl", hash = "sha256:9b4104bf43ca0cd4e6f738cb86326a3b2f6eef00f417bd1e7efb7bdffe74c539", size = 102394, upload-time = "2025-11-14T21:57:36.703Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/a3fa40084558c7e1dc9546385f22a93949c890a8b2e445b2ba43935f51da/ruamel_yaml_clib-0.2.14-cp314-cp314-win_amd64.whl", hash = "sha256:13997d7d354a9890ea1ec5937a219817464e5cc344805b37671562a401ca3008", size = 122673, upload-time = "2025-11-14T21:57:38.177Z" }, ] [[package]] @@ -1630,6 +2503,97 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, ] +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289, upload-time = "2022-05-26T13:35:23.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, +] + +[[package]] +name = "semgrep" +version = "0.112.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +dependencies = [ + { name = "attrs", version = "21.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "boltons", marker = "python_full_version < '3.14'" }, + { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "click-option-group", marker = "python_full_version < '3.14'" }, + { name = "colorama", marker = "python_full_version < '3.14'" }, + { name = "defusedxml", marker = "python_full_version < '3.14'" }, + { name = "glom", version = "22.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "jsonschema", version = "4.17.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "packaging", version = "21.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "peewee", marker = "python_full_version < '3.14'" }, + { name = "python-lsp-jsonrpc", marker = "python_full_version < '3.14'" }, + { name = "requests", marker = "python_full_version < '3.14'" }, + { name = "ruamel-yaml", version = "0.17.40", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "tqdm", marker = "python_full_version < '3.14'" }, + { name = "typing-extensions", marker = "python_full_version < '3.14'" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "wcmatch", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/00/6850f808965fc531c7287e7b8ac71daa13ce6c819ac23acb35beff5c2710/semgrep-0.112.1.tar.gz", hash = "sha256:2140a0fd16f3ac0a0e330980613ba65fe58faea7808a965e219c4199bab96102", size = 195076, upload-time = "2022-09-08T02:11:29.173Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/ac/263e8e91705635a09d6bf3cf2a2c3c082e2d9518f11fea9a0551c31bf8d0/semgrep-0.112.1-cp37.cp38.cp39.py37.py38.py39-none-any.whl", hash = "sha256:2a62bc6321d371dadc41df2c68678e87df807ddfa3d1faafb40bd0998abfe503", size = 25614590, upload-time = "2022-09-08T02:11:18.378Z" }, + { url = "https://files.pythonhosted.org/packages/12/01/9fefe9cc72117c0a70467d3395dafc0044205bf25f89d1d8d3e6dbc524d3/semgrep-0.112.1-cp37.cp38.cp39.py37.py38.py39-none-macosx_10_14_x86_64.whl", hash = "sha256:b690a84cbaa6a4670ff6c11a674818b8ad99736e89520a3c47d1542cc45e6d05", size = 22232420, upload-time = "2022-09-08T02:11:22.832Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2d/091319e7146c6dc33bf8123e5e8186803bd330f1f7b3ac7995c2fa95933f/semgrep-0.112.1-cp37.cp38.cp39.py37.py38.py39-none-macosx_11_0_arm64.whl", hash = "sha256:bc2437f12e41ac5e28a303e463098565a3d948c7744f6745bdb0fea341ccdae1", size = 25508160, upload-time = "2022-09-08T02:11:26.813Z" }, +] + +[[package]] +name = "semgrep" +version = "1.156.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +dependencies = [ + { name = "attrs", version = "26.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "boltons", marker = "python_full_version >= '3.14'" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "click-option-group", marker = "python_full_version >= '3.14'" }, + { name = "colorama", marker = "python_full_version >= '3.14'" }, + { name = "exceptiongroup", version = "1.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "glom", version = "25.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "mcp", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-api", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-exporter-otlp-proto-http", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-instrumentation-requests", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-instrumentation-threading", marker = "python_full_version >= '3.14'" }, + { name = "opentelemetry-sdk", marker = "python_full_version >= '3.14'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "peewee", marker = "python_full_version >= '3.14'" }, + { name = "pyjwt", extra = ["crypto"], marker = "python_full_version >= '3.14'" }, + { name = "pywin32", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, + { name = "requests", marker = "python_full_version >= '3.14'" }, + { name = "rich", marker = "python_full_version >= '3.14'" }, + { name = "ruamel-yaml", version = "0.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "ruamel-yaml-clib", marker = "python_full_version >= '3.14'" }, + { name = "semantic-version", marker = "python_full_version >= '3.14'" }, + { name = "tomli", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.14'" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "wcmatch", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/8d3079d353686db515a1188d6110301e1edff75abcffbfaee99a55f21344/semgrep-1.156.0.tar.gz", hash = "sha256:6c58c499ad86385c4786f04fe387d788113f1b9c95bc7b4c38941bf078cf5d9a", size = 52953549, upload-time = "2026-03-17T21:53:47.818Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/d4/9f8efd20f96cb9ff74d5dd013b1938b9246001c4b61e8e98501f236af71a/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_10_14_x86_64.whl", hash = "sha256:5bd2924958af5d4e199fe82c4ac9c7be4ae4fd20a3a46ce9f71237f8408f5b66", size = 42194313, upload-time = "2026-03-17T21:53:25.492Z" }, + { url = "https://files.pythonhosted.org/packages/66/a9/3d4082f30bca4ba738d391e241174c2297be87bb5ca37a3911b164289694/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl", hash = "sha256:ff57b35def987ec3f21748051fdb89ae57574984bf8108b03d79473da49e93f0", size = 45891219, upload-time = "2026-03-17T21:53:28.903Z" }, + { url = "https://files.pythonhosted.org/packages/60/32/dca58fbb76a0d5ba16ed366e3478c50c737bd3da8788eb0d213d60562075/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux2014_aarch64.whl", hash = "sha256:7e9ae909fa8c26220c88392af9c22479c0ef51e170eeda8b2c81382b431afb76", size = 60020097, upload-time = "2026-03-17T21:53:31.923Z" }, + { url = "https://files.pythonhosted.org/packages/92/0c/b00156bee40cf9e594c2bd10672232e33d9a7d1bc8b5a6dd697097c8d6be/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux2014_x86_64.whl", hash = "sha256:a7f68544bcd33fac9bc519e6a6c4759d612836422ce87bc08dd014481c3e8fd0", size = 57680875, upload-time = "2026-03-17T21:53:34.967Z" }, + { url = "https://files.pythonhosted.org/packages/66/9d/1a82d535e5a54e9617efcf3b8e9ffecff091566471946dd9369cd586c3bd/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_0_aarch64.whl", hash = "sha256:9604dddeebee8a3acc89f6ac25ad646e222a753a04fb944c2c5635f615b3a242", size = 60020098, upload-time = "2026-03-17T21:53:38.203Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3c/3d48471dbfcb66cfb22b175d2221bc538355edea988f62cf1abb886afd8c/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_0_x86_64.whl", hash = "sha256:b68d14aced6c6f389fc7524e314b5638aa11ba50f35d3d3d97488e0930a21beb", size = 57680873, upload-time = "2026-03-17T21:53:41.211Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3d/f6f42ee18039e0f655ef5ddcd68ef2447b4c922178879fd1e9a75b846d41/semgrep-1.156.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-win_amd64.whl", hash = "sha256:a9a683f94f99629d345ac73a215859fc5ce6c58fe67ad2f78671de3874a260e3", size = 53820998, upload-time = "2026-03-17T21:53:44.318Z" }, +] + [[package]] name = "sqlalchemy" version = "2.0.46" @@ -1686,6 +2650,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/a1/9c4efa03300926601c19c18582531b45aededfb961ab3c3585f1e24f120b/sqlalchemy-2.0.46-py3-none-any.whl", hash = "sha256:f9c11766e7e7c0a2767dda5acb006a118640c9fc0a4104214b96269bfb78399e", size = 1937882, upload-time = "2026-01-21T18:22:10.456Z" }, ] +[[package]] +name = "sse-starlette" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "python_full_version >= '3.14'" }, + { name = "starlette", marker = "python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/1b/bc9e3e7a72dcdad7dc7888758f5d00f56f8909ed5cfdff822bd72bb4c520/sse_starlette-3.4.5.tar.gz", hash = "sha256:83072538bc211a2f68b7b0422226c4af3e9b62e106e07034664b832ca019842a", size = 35249, upload-time = "2026-06-20T17:36:58.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/75/c88d3f5dafd59c791da1ce27650d30bf5b70cbf1cbf01cd00e5f9e360915/sse_starlette-3.4.5-py3-none-any.whl", hash = "sha256:e71bad53323f65573c3864a6c3bd0c1eb6e5f092b2e48082b0c35927d19ca296", size = 16518, upload-time = "2026-06-20T17:36:56.729Z" }, +] + [[package]] name = "starlette" version = "0.50.0" @@ -1699,6 +2676,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] +[[package]] +name = "stevedore" +version = "5.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/88/35e4d27d9177d7df76d060e0a18f69c6c5794c96960c94042e20a12c8ba2/stevedore-5.8.0.tar.gz", hash = "sha256:b49867b32ca3016e94100e68dbf26e72aa7b8708d0a3f73c08aeb220370ac715", size = 514710, upload-time = "2026-05-18T09:15:27.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/ac/19f9941c74add59d17694930ec8105d5eddeee4ce56dd8632b765ca16d6c/stevedore-5.8.0-py3-none-any.whl", hash = "sha256:88eede9e66ca80e34085b9174e2327da2c61ac91f24f70e41c3ad76e4bb4872b", size = 54553, upload-time = "2026-05-18T09:15:25.82Z" }, +] + +[[package]] +name = "stevedore" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version >= '3.11' and python_full_version < '3.14'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/dd/04d56c2a5232358df41f3d0f0e31833d378b6c8ed7803a6b1b7867b0eba6/stevedore-5.9.0.tar.gz", hash = "sha256:abbd0af7a38a8bbb1d6adea2e35b17609cf004eaac323e88a8d8963640dd2b3c", size = 514850, upload-time = "2026-07-02T11:38:08.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/8d/008761f6e1000600e5303db30d05724bdcf3d2d186cbb59fac79b52e39ed/stevedore-5.9.0-py3-none-any.whl", hash = "sha256:e520945d4c257700eddc1eb1d79df04b2ea578eef185e0e3fa5b442fc848d3f7", size = 54463, upload-time = "2026-07-02T11:38:07.43Z" }, +] + [[package]] name = "structlog" version = "25.5.0" @@ -1711,10 +2714,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, ] +[[package]] +name = "tomli" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, +] + [[package]] name = "tomli" version = "2.4.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, @@ -1765,6 +2785,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] +[[package]] +name = "tqdm" +version = "4.69.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.14' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/69/40407dfc835517f058b603dbf37a6df094d8582b015a51eddc988febbcb7/tqdm-4.69.0.tar.gz", hash = "sha256:700c5e85dcd5f009dd6222588a29180a193a748247a5d855b4d67db93d79a53b", size = 792569, upload-time = "2026-07-17T18:09:06.2Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/21/99a0cdaf54eb35e77623c41b5a2c9472ee4404bba687052791fe2aba6773/tqdm-4.69.0-py3-none-any.whl", hash = "sha256:9979978912be667a6ef21fd5d8abf54e324e63d82f7f43c360792ebc2bc4e622", size = 676680, upload-time = "2026-07-17T18:09:04.172Z" }, +] + [[package]] name = "typeguard" version = "4.5.2" @@ -1807,12 +2839,139 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] +[[package]] +name = "ujson" +version = "5.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/89/7a/c8bb37c8f6f3623d60c33d15d18cd6d6655d0f9c3eb31a9969f76361b199/ujson-5.13.0.tar.gz", hash = "sha256:d62e3d7625384c08082abad81a077af587fdef2761bb14c3822f4234b8d07d75", size = 7166784, upload-time = "2026-06-14T22:36:50.209Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/a4/ff15f528d386f47b1972859f25587da017d5be84c75076b380fedde318fe/ujson-5.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:770643b4752266c5a466149848b78c3874940926a4ecef304f518b2b6cdb432f", size = 56497, upload-time = "2026-06-14T22:34:58.806Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d2/92e83af35cd65d3b7c47fb0e24927aa2b478897af8b0dd0ee19abcd50c03/ujson-5.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8e8c1203cb1a27720debc334f840a9170da741503522f86999710cb4738fbe3", size = 54301, upload-time = "2026-06-14T22:35:00.301Z" }, + { url = "https://files.pythonhosted.org/packages/96/9c/3eb2d21778dbd0a9f1adae7ce73e4f6c981efa7fab534eb28afde31fd25e/ujson-5.13.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:65c1813fdd742fe3c249d9c417fa490e5b54e8a91bf343a88486ec50d175c444", size = 59968, upload-time = "2026-06-14T22:35:01.612Z" }, + { url = "https://files.pythonhosted.org/packages/9a/52/f5b72747349c66552396166a13424a717df70e76a18ac5bbedc84c244407/ujson-5.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5aa9bf16f0131812720dd4ae70bc1a0cf68f79e93c07c66100d328e75944b567", size = 53434, upload-time = "2026-06-14T22:35:02.644Z" }, + { url = "https://files.pythonhosted.org/packages/4b/73/103c2c6df7bfe57e01fb6006551188b4dc9f873cb5f72146bd5b177b75f9/ujson-5.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82c17b904c03c2b9629486ec91a8fa46a15f10d03504284f54ed7257a917d9f1", size = 54976, upload-time = "2026-06-14T22:35:03.759Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ba/44fcf17cc93be7c6647fef2e556d703791659280e6409623dea854e6db7d/ujson-5.13.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0b5f6983b2469db00e540b68fa8297b7a0ccd0d5173c60cc3e84f336b09395f", size = 58239, upload-time = "2026-06-14T22:35:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/0d/60/10f6c92b549cff72e555a900ee7128b992ebf510bad0eebd946e72b68ecb/ujson-5.13.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b877fe7926107d25d54b655c5b8dd94b294b22c157233163ad29fdb54ac10cf4", size = 57876, upload-time = "2026-06-14T22:35:05.998Z" }, + { url = "https://files.pythonhosted.org/packages/71/5e/b8b2806996c75d4c34cf92ec04c5fff58a645a32632f27836886a718cb87/ujson-5.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f3cae1c811e787b9500e2830af8632dcb32a78dea5baf15aac51d681c59a59dd", size = 1037734, upload-time = "2026-06-14T22:35:07.081Z" }, + { url = "https://files.pythonhosted.org/packages/d4/02/baaf3ad0bb4b2c3bec3d9ff8a59768d39d20201146742e8581f8a0f87e77/ujson-5.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:820b78a6a183ab6591b2ea888020032ef0fe328d852af9a5c8d8084ababb2218", size = 1197048, upload-time = "2026-06-14T22:35:08.319Z" }, + { url = "https://files.pythonhosted.org/packages/8e/aa/a945aeba9d463e335169b3c0ee60adc147e22e80bb636fe0f185bed1247f/ujson-5.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1209c985d1f4c2ae085ced7325509650cdb3533bb7294558dd1f26d48377942", size = 1090118, upload-time = "2026-06-14T22:35:10.014Z" }, + { url = "https://files.pythonhosted.org/packages/cc/68/57e084026ab657ab0443017fd0aa51009300bbd2eb09f5113af113703e8b/ujson-5.13.0-cp310-cp310-win32.whl", hash = "sha256:326553ae6c063c8246974906a6c137a0780fe46d143abebc52cd2cadda0f1814", size = 40008, upload-time = "2026-06-14T22:35:11.276Z" }, + { url = "https://files.pythonhosted.org/packages/56/8a/da9bb80765a5ea582ff8c1bd2f3c3909c05733314c55652d1c0bd34514f8/ujson-5.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:0354d6b50b0d153ed7c629845b18a953d0c727b7e768fd94a94e0602abfa1f29", size = 40947, upload-time = "2026-06-14T22:35:12.323Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b1/eaf4308ff7e7dc5aba78d7479102a69fa5fbe65e1b02f16c62b553a0b506/ujson-5.13.0-cp310-cp310-win_arm64.whl", hash = "sha256:7b4a05f61a96553995da6a4d502e07bc8aa5d07a90031df006239c6b00ce1c83", size = 38975, upload-time = "2026-06-14T22:35:13.218Z" }, + { url = "https://files.pythonhosted.org/packages/58/dc/2fcf821896803248122835c800e74f4582de9d6092efb37152acd7f79bdc/ujson-5.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b7badefa73f96bad9e295ea22bd06967b851c8aad68c74196437e3584f25de5", size = 56496, upload-time = "2026-06-14T22:35:14.362Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c6/83db69f96dc12509c9510084c0389c4aff4dcf427f9b613d1a23abd446ce/ujson-5.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:09effd42924a80df20a63b31a1ede905e66b0ce24aafe7a4cbedb05c783f8bb3", size = 54300, upload-time = "2026-06-14T22:35:15.522Z" }, + { url = "https://files.pythonhosted.org/packages/f0/6c/6d87206988172015781b9ff842c0a7eca897c026b2e7a95e11d86d46ddf5/ujson-5.13.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:89d0bfc986d02b4ce76b00e0f560bc8d30dfe8c05a1bfd8529e085eb6c1a77d9", size = 59976, upload-time = "2026-06-14T22:35:16.636Z" }, + { url = "https://files.pythonhosted.org/packages/ee/43/d28ca5e6c3d8c467a4c6296404067f4eee9d67dfeeebeb3c5fb0bd6cb958/ujson-5.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cdd9618e07b3b142a02f0ab8227fd52453688b8e8e60ac0511f13a25fa8009db", size = 53476, upload-time = "2026-06-14T22:35:17.664Z" }, + { url = "https://files.pythonhosted.org/packages/a9/99/d6bb0e18188954326b38499ae61b04ce8ead6425b8844384e537a491267b/ujson-5.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0806683e8171ec06817e6af22f14ba0cd2f16def8a2ffb22a28a10615249355d", size = 54962, upload-time = "2026-06-14T22:35:18.693Z" }, + { url = "https://files.pythonhosted.org/packages/c6/5f/8f1ce659a59ef9510fa47b95773442049a383c533a645b2da8beeeec90f1/ujson-5.13.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1cf46c79498c81f088cad4165b1669a78bba7bfbeb778c7cc1aff316e062b0d", size = 58265, upload-time = "2026-06-14T22:35:19.775Z" }, + { url = "https://files.pythonhosted.org/packages/ad/8e/9d11af9d1d19ea239b0d289cf62a611cb4f2da9ec0d46b826767f4ab1768/ujson-5.13.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2282039eb26f08ed1a1381360395f8a310f39a45ef7314cb0f258a7d2917ea3", size = 57874, upload-time = "2026-06-14T22:35:20.896Z" }, + { url = "https://files.pythonhosted.org/packages/25/e2/7891c4a1c954307ab6a575686897a4963aad36c284742216f52dc40cba4a/ujson-5.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c75eb7fac0fe92925b959cf2fa18f88d9fb76b10781fd2a6ccb895d5fb89171", size = 1037746, upload-time = "2026-06-14T22:35:21.964Z" }, + { url = "https://files.pythonhosted.org/packages/89/4e/8a4ce87d3eaaee398f4238f74f128c6eb34269f041659995b2c09710ae7c/ujson-5.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:12078e81def2790140583abefc1b979f3c77be15d53c524bf0e232f669822052", size = 1197022, upload-time = "2026-06-14T22:35:23.183Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fb/a1d0a6a83a13adee3a13cad308dcd3251ac53c4bd781f737242ad2f10d19/ujson-5.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e12192a37c6c0e476554b62647acdf6139a47b6f13d8bad8dd2316763c4cee12", size = 1090116, upload-time = "2026-06-14T22:35:24.421Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6c/4aed5dce0161d6b8c95c5da760477602a05e2a540a762424395acad9aec0/ujson-5.13.0-cp311-cp311-win32.whl", hash = "sha256:ae53b3f046529c193d533ca8111492330b204d6007611cdfa20e8b764c7c1389", size = 39974, upload-time = "2026-06-14T22:35:25.779Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c5/f9f3cf19f6ac29e07782eafed562fe0a7cb451b44bd1664c58fe8974235b/ujson-5.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:2275bbaaea3eddd2e8ec0863e28a420f5f520b14a760fc3f1e49fd07a974448c", size = 40941, upload-time = "2026-06-14T22:35:26.774Z" }, + { url = "https://files.pythonhosted.org/packages/15/ec/46058bbbbe45e054cdb9f1dc0bf416fbbd5fec06bf3b4987c2437e496350/ujson-5.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:32a59e7151fe2fec8fdf9a565ee66fcf87918d48827e21cdab5e15ff9f274b78", size = 38974, upload-time = "2026-06-14T22:35:27.871Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ae/b66deca15da1f7faf6952d8eddf55978482bcbfd294ed2afe2c526ea325f/ujson-5.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bf81570ac056cb058f9117b52ca5dd800bfe9381d0076d0bb30a08a54591d654", size = 56743, upload-time = "2026-06-14T22:35:28.863Z" }, + { url = "https://files.pythonhosted.org/packages/88/4f/b03bcc9eaf4621ac9008dec90918d8fb4839d611666cb99eb255696c67fe/ujson-5.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7edf16359c52ed53406e216565d83e6b98c23c3cb9a0a01673f2493f8fb15edf", size = 54390, upload-time = "2026-06-14T22:35:29.857Z" }, + { url = "https://files.pythonhosted.org/packages/77/79/f98c6c1a4ed9d92d39d5d2d133f2b6fce5da11ea50c341117aedde8011c4/ujson-5.13.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:24539618fb3243cfdf27dab9a850acab80798a01501e9586b61fb9ecd016a891", size = 60047, upload-time = "2026-06-14T22:35:30.857Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/f68e14cf476d149945211142f4c20782c1f232c489e8edcc4f4b58ce4997/ujson-5.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fdde6341d213b29f413b5fa9fad1392d5408074c75f0900ed949e97e546fa5df", size = 53437, upload-time = "2026-06-14T22:35:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1a/5718237cf4141e5be46ff371387e90b01f27774cb6f0f79ff4803a2430ca/ujson-5.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:229faf041ef249ee3fd57bac1cedb123d2718ab63f6ccd50eca95ea902eb0dca", size = 55057, upload-time = "2026-06-14T22:35:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6f/7f55c1e9e0be87beebaed553fa186ad5f6d5d639cbaa9d49f78f2f91c3a9/ujson-5.13.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d02f31c2f59cc6a1c2c3633b377701fc2d8e876cc01950735d7a01132ccc233", size = 58186, upload-time = "2026-06-14T22:35:34.055Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c4/9a34ade542426f56a0bc042f774073d1c247ae7575363c27587788cb2b2f/ujson-5.13.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea7204e9fa7538bfbb1396e1ee8c2bbcd3818b3633ef5bb14d4fdea52994d14d", size = 57935, upload-time = "2026-06-14T22:35:35.05Z" }, + { url = "https://files.pythonhosted.org/packages/36/06/407633f0709e168107f56368bd5a0fa8fe07acd7f1d3000710bc0bb07470/ujson-5.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c5a2478a3a1fa4421f7e035b87194eea0cf44c7971a3f32ad1b42a0dfd63c03", size = 1037685, upload-time = "2026-06-14T22:35:36.022Z" }, + { url = "https://files.pythonhosted.org/packages/c3/df/eb5bd92dc1b23254fea5b2022007baff5491a7478bfcf7e9260d3a10f1ac/ujson-5.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b535e0970c96957e999cfe5ec89361f0e8d0bb987fb5d5144f6f495cb3ed9e19", size = 1197141, upload-time = "2026-06-14T22:35:37.38Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1c/65f2ce1a0411ec9a87339db01f0d5d554a49c4248ec68ab52a1b7e14e9c4/ujson-5.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d0ad1207694988498fca7e0bb28eba7564fa33261d2f9fdf66a3aaab376b803", size = 1090225, upload-time = "2026-06-14T22:35:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/73/53/310aabff0704f9c7ef0d3f431ce8b8e3147c3cca25334a205615c511f65e/ujson-5.13.0-cp312-cp312-win32.whl", hash = "sha256:d6bc9fa43a49e403c68c7eb164eef0feee9dd29474a7c6e0d3b6267025371990", size = 40075, upload-time = "2026-06-14T22:35:40.44Z" }, + { url = "https://files.pythonhosted.org/packages/b5/23/d3536d8945d1bb00248d998c8dcbe678a884681ad181072daecfafe4eea6/ujson-5.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:6692d49ff970aaa5008f4a6fe06974bc91fd957bf13173f765e46d8ba44906ea", size = 41097, upload-time = "2026-06-14T22:35:41.39Z" }, + { url = "https://files.pythonhosted.org/packages/72/a1/4b147c06ee5bb14bec6e26786358c8510c4d75e28b88146a6ac7620f1f71/ujson-5.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:5737ffe0740a788b0e6255f0ffb281db49305fd6e6a587be44c73d9e92b554c4", size = 38875, upload-time = "2026-06-14T22:35:42.357Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f1/fe8a467d8ff5821e076b96f398d3acfe3cd568d900e6ccb41b215592b152/ujson-5.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46998fc8d11aec34a20e2010905e7059732a3d192d9a3c3fe4f9ffd146c87ec8", size = 56746, upload-time = "2026-06-14T22:35:43.398Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/c7ab82d6471dfa7e4fd68ae6ff2c6a50d077c05d6ecdea0cec8af635b2c4/ujson-5.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ee03ce288ba25b05cf0de87203165642277a25caa4f00a437e13152e5214e310", size = 54388, upload-time = "2026-06-14T22:35:44.586Z" }, + { url = "https://files.pythonhosted.org/packages/10/e6/4e9e998d991ff88bbc93b21daa63bba2baa61c6f952dbcec937cf7304ebe/ujson-5.13.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:cdf33b588a81b05d0b585c66f83050c49cb670623424d10e4d1ad37ba2f7eed9", size = 60051, upload-time = "2026-06-14T22:35:45.567Z" }, + { url = "https://files.pythonhosted.org/packages/9c/11/876dff43f05417a01c6119f0fa10e01f1226631c5927ef08f56876b2bb67/ujson-5.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cabd73c114ce93c21d7db2e2d8e16217fd8a5b2b3ec754629eebef5c262d47f", size = 53438, upload-time = "2026-06-14T22:35:46.623Z" }, + { url = "https://files.pythonhosted.org/packages/09/02/f9dbf6c3e46d700eb1d9ed637567221a06eeb1ec289633be992ef54d7a34/ujson-5.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ffc61fc756a64f4d169a78cc638d769e3c324f45fc51997626abf4e5e5dd6460", size = 55060, upload-time = "2026-06-14T22:35:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/bc/3d/7e49a70265a1e5ed1b5e8edd5f54d57ae41e2134faeae9b16f6f5a0eae20/ujson-5.13.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c00323c13a35822c9a67a26c0b2a0787510bf1ef490922b58009b362d1a3e21", size = 58189, upload-time = "2026-06-14T22:35:48.617Z" }, + { url = "https://files.pythonhosted.org/packages/66/34/b64278f67e19052f09810576c7e50b3da8d4f5218b226046324d4d5c24b4/ujson-5.13.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496e662a6b46d5f936d77fb68259cece19213bb2301ddd520dbd75ac7c90c5f4", size = 57941, upload-time = "2026-06-14T22:35:49.674Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8c/51513357a5c75bf3e5bae46accfdb3e6e6f5caeb72ca8b253ec45ba853fb/ujson-5.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7fd41b86444df14f8b4b7afaaa9f27bacfbf8c18380872317aeab6cd125dcede", size = 1037688, upload-time = "2026-06-14T22:35:50.699Z" }, + { url = "https://files.pythonhosted.org/packages/54/5a/dc6afe071d6b977390d2dc41e15800a2716f317988dd03187cffe7b4d624/ujson-5.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bf3c2c4ea55d4187903fcdc689a9bf5b0fc72d8c0eaff39db18c1f337c8832c1", size = 1197141, upload-time = "2026-06-14T22:35:52.052Z" }, + { url = "https://files.pythonhosted.org/packages/50/23/b473d101412c68527cb502a8728f96ab307aa7bfa75d6ea2037e2c7f74e8/ujson-5.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b6eca7751d61045a9b1e7f9a8c97ac24b164f085b60bef1c4668654bb2338011", size = 1090235, upload-time = "2026-06-14T22:35:53.589Z" }, + { url = "https://files.pythonhosted.org/packages/fa/0a/8e583cce90f9f91ca1bedb3e628b6f5642aed91feb29b197431268d4c4be/ujson-5.13.0-cp313-cp313-win32.whl", hash = "sha256:b63d3820f978bc8e98cc3f1fe26a33b0d2ea237733a23fe5e9cb5d51f466bd97", size = 40069, upload-time = "2026-06-14T22:35:55.019Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b5/1fe203bc294e98fdd65606883692ad8dc0aaac73838b89c99c3513404424/ujson-5.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:17a59d5cf23ef98f7c9314524976b4b288374d83200add01d953024fb06404f9", size = 41098, upload-time = "2026-06-14T22:35:55.966Z" }, + { url = "https://files.pythonhosted.org/packages/50/5e/aceadce24fdb7cbc67f02286b1d4e91a575aaef5afb876c9908d6e6e5769/ujson-5.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:b49516fbe803ff30d6caa9ccc3799ec7f968992747ce3099eae4758928577b53", size = 38877, upload-time = "2026-06-14T22:35:56.936Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/b5139d696f5328f3cab70b9ec046f15e3f49497a4de6280974640602f539/ujson-5.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cc9dfd41fed397ab03bb9d9fe1cbd83301211c772a17536033ce7d68877ac82b", size = 56897, upload-time = "2026-06-14T22:35:57.974Z" }, + { url = "https://files.pythonhosted.org/packages/53/55/477183aeddfdf0f88ae039ffee0ed866cfb993da0c0c9aa915807554aef8/ujson-5.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ca7ef2fa6c408a7c0f558e4d33d93b32ddc35ed6d3cfc505747931a64b7465d5", size = 54451, upload-time = "2026-06-14T22:35:58.932Z" }, + { url = "https://files.pythonhosted.org/packages/ea/63/55e5f23e156b4c8bca095d828b4cd3180c0b42aa3501ef88836d79606fea/ujson-5.13.0-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a554b2e5bee85030369514cef8b0b913cebe1a4c2c0c13541966d50bcba22b1a", size = 60053, upload-time = "2026-06-14T22:35:59.969Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/08c6cf5548bd6f4bb557c9fa7e8edf87324bb04c17249d1966028d61dde0/ujson-5.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea939ff629ab03ae970d03eca6d1febd8ed55ba38ca44aec64ce997537cd3fa0", size = 53481, upload-time = "2026-06-14T22:36:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/6e/b3/0ac9a03551467784067f505df1bb875c639ba32f1da79ce467ab15911ada/ujson-5.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b98bf2faa5e37ecfe752226ea08290031e375a0c43d425a0b955fb3e702a2a71", size = 55058, upload-time = "2026-06-14T22:36:02.297Z" }, + { url = "https://files.pythonhosted.org/packages/ba/be/ec91029aec067174473d022fa0f6c3c1431a173f888d7599739f05c668eb/ujson-5.13.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a4b92344b16e414aeb609e57f62c466500e53c94f1698f5b149dc0b7223ec3e", size = 58225, upload-time = "2026-06-14T22:36:03.321Z" }, + { url = "https://files.pythonhosted.org/packages/29/33/a948f329252ece3f9c93d177243de6e677927ebc6ac44256742dbbef3c39/ujson-5.13.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df805aad707507a1fa165fb716218ca3a89f142125dc4b23c9fcc08fa402d97", size = 57930, upload-time = "2026-06-14T22:36:04.385Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0c/c33655218b8e0a8adbf066de0b999cae5c324061f3eaa4dda17423145d9e/ujson-5.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7576bdbef327c3528f011002a2d74486f6fe4e33289bdb7a042b7f1a6e9d8285", size = 1037728, upload-time = "2026-06-14T22:36:05.467Z" }, + { url = "https://files.pythonhosted.org/packages/2c/bd/d286947525ea7ce3f2d8dc55c15b9ffbe425bc455c96af7b8f8a402599a9/ujson-5.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6eee5d7cce3f32a468905f9ff61807a60287a90258d849460f6fa826e810870d", size = 1197146, upload-time = "2026-06-14T22:36:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/3c/3c/9eb916377050b0785f048a34588c1c390ddd41ae00b78db68ee1ad022356/ujson-5.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:144e9d8a454cfa727e0f755e1863738ed68068583bda5463052cb446835bd56c", size = 1090223, upload-time = "2026-06-14T22:36:08.329Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5f/242fd97a2628b842d4bfaa9b18e1f68187f934d67503291ebbaab1254637/ujson-5.13.0-cp314-cp314-win32.whl", hash = "sha256:576f35c35b918d67d41b933878062ec0a5c9f4d1e9e14e04aeef35384963feae", size = 41223, upload-time = "2026-06-14T22:36:09.644Z" }, + { url = "https://files.pythonhosted.org/packages/23/f3/7f2bd9ca0c507142d0c22347b3d6f8803be1d8851c31707e57f5923fdbea/ujson-5.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:d5e206e9f849ead27e51ef8da44e52b38da7c6dbd929a7340ab44533edcda8d7", size = 42265, upload-time = "2026-06-14T22:36:11.043Z" }, + { url = "https://files.pythonhosted.org/packages/b0/29/3e9a8fba321c031315f6d263510969a5d01f41fc471b5be107e413c1b2f8/ujson-5.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc470179775468f9a007d3a6a2734624248c94bf47c6645e808c7e50a5070d1a", size = 40205, upload-time = "2026-06-14T22:36:12.286Z" }, + { url = "https://files.pythonhosted.org/packages/12/e9/1c543837c6a3c6672361882a0fa269bd02daf9cc4c0ca88a9dccd9df98d9/ujson-5.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:69b4e36bb7d5f413ba8c00c8006b2ec627cc5ace97301462f6aadb66ec9d2979", size = 57402, upload-time = "2026-06-14T22:36:13.238Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/39862f0f7174ff07cfd1e2d0c9065ded34aeebdb7db8daf2f0e5bf89b46f/ujson-5.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b644d50f66de5490c1823c7176618cead5e8e8a88cba9f40a6308ca52e79267", size = 54973, upload-time = "2026-06-14T22:36:14.432Z" }, + { url = "https://files.pythonhosted.org/packages/02/66/f53d3b32c3f177f846ca6b624e832f29000d8a213a2d8768e254bd470ced/ujson-5.13.0-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:15107aaa4f559d55201165ec32abb35c283a861be1fa67229578cb7d93fcd93a", size = 60683, upload-time = "2026-06-14T22:36:15.806Z" }, + { url = "https://files.pythonhosted.org/packages/e6/d4/dddc4646d2633c85c938c2ded7d5a9711cdad5be1e13b31b7dad76f61c83/ujson-5.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6e343c5f0c058523f1edbf6ae4eceb4e0d934205a53bbdd8d9a945c83324662a", size = 54167, upload-time = "2026-06-14T22:36:16.952Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c0/d8608c3f4d3f05e6441364b63fde1d279700135c1a6577a773662c07fbcc/ujson-5.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02200035bc80e830f076ffc1b329a94c295aee6d9de8c9043647cb9a7bd4f76f", size = 55568, upload-time = "2026-06-14T22:36:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/22/8e/dd12b735aaba0806c3d70c18184d50e1f9712e0757c7c0a4f376450cfe28/ujson-5.13.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f19b81b73ff28f5c5022ee794f94122bfcda07a76423078e349465d71223a1", size = 59086, upload-time = "2026-06-14T22:36:19.071Z" }, + { url = "https://files.pythonhosted.org/packages/48/43/ad41e8752d5ec3a590a5e7b426a54e36b7aab911d9b5a4f7384dc62507ab/ujson-5.13.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82e1393e6dbe3c95fdfc95c6c528890e191351a1f024ef51126cf1f22543af52", size = 58667, upload-time = "2026-06-14T22:36:20.171Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8e/b44a6afb77b94118655c029081b7932d64bb4c5b1c8ba2b7f5808b5d0bc2/ujson-5.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38afcf994b28ed85ea2420e2a8d79a37d0a77348b3daf53850c16edda66f942d", size = 1038553, upload-time = "2026-06-14T22:36:21.245Z" }, + { url = "https://files.pythonhosted.org/packages/7e/93/fab1d786174c8780eb3e386c73f1925a435e97fbf77c957fea4fca83994d/ujson-5.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1bdf2518971586f2b413156c49d9dd8b56cc990a8647081e1bd00af60564d469", size = 1197938, upload-time = "2026-06-14T22:36:22.585Z" }, + { url = "https://files.pythonhosted.org/packages/f3/bc/2f073bb708f9d128f5d1cb39063a5f6421b1ce94c61be8661c55a189f407/ujson-5.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:751ad01042472f1c7c02f5c597c7aee79834e82a6cc384ca302173bbc8e8deb8", size = 1090938, upload-time = "2026-06-14T22:36:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/cdaa50bba29d7dc9eb19212755b09bb96f56596e75957c3717c6b85454de/ujson-5.13.0-cp314-cp314t-win32.whl", hash = "sha256:74f3dd61aeb01b7b2a6754e400224e819279041b3867935a55ccf57fb86a43b2", size = 41802, upload-time = "2026-06-14T22:36:25.418Z" }, + { url = "https://files.pythonhosted.org/packages/bd/66/a6e669e90083febdf6c0600d3807f6017fd4d3962d5bd6ddc605c73a06e5/ujson-5.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5c31317d5e4504dae8f98795358b6082fc0ef96e7394806db0a76a4a8717f446", size = 42790, upload-time = "2026-06-14T22:36:26.614Z" }, + { url = "https://files.pythonhosted.org/packages/3d/5f/fcc6c6a9d711fd8b020ca8ff65148212f0a712c809d173cd949e58de68c6/ujson-5.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aefd3c9c95f9b62348956396ff7b31818476f8f54dc4a4e64cbd4f0491db6fca", size = 40708, upload-time = "2026-06-14T22:36:27.721Z" }, + { url = "https://files.pythonhosted.org/packages/30/70/dbdd277d64bd3a149532567ceb082fe26f4ead58c39e0a97566ccbdf14a3/ujson-5.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3e074a1f7778d58aa3b3056bab7b6251aabb3f381808018ca2b7fb8dbdeef7ab", size = 58393, upload-time = "2026-06-14T22:36:28.702Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/592c70af94a67cafacd9c840ae2980f27d511dde2732a4c0dfac8f176ae8/ujson-5.13.0-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bb53ef95d35875262b8d0aa28506ca612ddd07058bee2a90f609938e69dc801", size = 54447, upload-time = "2026-06-14T22:36:29.802Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9d/2bb91e1e25a8584cb3b63544b9bd26f621173535c77ac6cae13bad8e7904/ujson-5.13.0-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fb296a0aa480ab88d895ddaa90372604c08ccc72323f02590612c775426ab413", size = 56066, upload-time = "2026-06-14T22:36:30.806Z" }, + { url = "https://files.pythonhosted.org/packages/76/ec/8e3802fc4a4e31e817b972bbb0e704a484d8c75ec349b3feb45fa9fb54c4/ujson-5.13.0-graalpy312-graalpy250_312_native-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2862f81af44b3a7e74c5d80caa118d736be1991ce6f1d5c723716fa403060cc6", size = 54938, upload-time = "2026-06-14T22:36:32.051Z" }, + { url = "https://files.pythonhosted.org/packages/4a/48/d0e3e511039b86fd1ecfe2bf761c800552d273ef8f19e71de93bf38a909e/ujson-5.13.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c16e07581172f08585b409246f4535dab13ee85af0e3d3cfa8684b653ca44fa8", size = 56115, upload-time = "2026-06-14T22:36:33.349Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/689613037fe691d18eae075cd141089f3a3156146be14512df92d8a9ae8f/ujson-5.13.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:9bd0f2dd05937c3b089af316884de18c6f6182ddb8ffce597d2e7c7a9ba9f447", size = 41802, upload-time = "2026-06-14T22:36:34.523Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7f/276f830bef4d530d50ff4d8f8c568002e7ebed9f64c06747b1d1c4325f02/ujson-5.13.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:96e7e019b097b4b25fccddadb369d13f412c13695fcf0680b6bf906376156151", size = 52479, upload-time = "2026-06-14T22:36:35.627Z" }, + { url = "https://files.pythonhosted.org/packages/fc/50/99b05555ef42a2ab2e26aa369c46bde6a64f8aeeb687628102e98cc78bed/ujson-5.13.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:7892ea6dd85ede6d30fbbd22af1239b9d81dabdf9b7a8f10ca6d4464d4d9b8ab", size = 49953, upload-time = "2026-06-14T22:36:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/78/15/399766e8ba002bd8e5e2e45828e0e12a5dbeb4145d6a604ba3787db5eec2/ujson-5.13.0-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:e1842e10adc8f0db0d3e8aa3a1f8b05ce0456b39e180c8553d7f36dd0bf24b6b", size = 55716, upload-time = "2026-06-14T22:36:37.833Z" }, + { url = "https://files.pythonhosted.org/packages/61/a6/825d92bce42cd442b57d89b4c20afc1737246321d3433c6194e5c35c6a11/ujson-5.13.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8bbb1f5ab810954fb307b6c5e68af58210c31da8569f9e6498a3958c1859e72", size = 48648, upload-time = "2026-06-14T22:36:38.878Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d6/7ea7a32f35457560806375193dbc4f0d8ffd8c8adae42f86686392a76c41/ujson-5.13.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f6d55c68985654a84a9b47ac51f2655adac4fd264e4189f832960c2270dcf70", size = 49947, upload-time = "2026-06-14T22:36:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/af/b4/0bd6449ae35b6026d94f4d1a32dbc9f40c969ed1d6e36a7e26ccf56371be/ujson-5.13.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b266f182d4bce74a6a9e1f86988485e8cd422efdcc7c3f537e00cc956f52678", size = 51149, upload-time = "2026-06-14T22:36:41.129Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2d/39da479a8461d1d78d533940a8426a84e23708a6a7a426f2178e04443252/ujson-5.13.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfaa8302eb9bb7f5e231f256caf83040760585e32751d19155c0f0c0225f8de1", size = 52456, upload-time = "2026-06-14T22:36:42.266Z" }, + { url = "https://files.pythonhosted.org/packages/12/cc/91ff81c50b85a158878f06d6e9153227bbc04209db291a152c286a0ee4a8/ujson-5.13.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:43dee3081b00fe447c5b9e2fe7ebfd57f7bcc5dd25b8a439c26c8c174dd581be", size = 41155, upload-time = "2026-06-14T22:36:43.455Z" }, +] + +[[package]] +name = "urllib3" +version = "1.26.20" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11' and python_full_version < '3.14'", + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380, upload-time = "2024-08-29T15:43:11.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225, upload-time = "2024-08-29T15:43:08.921Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "uvicorn" version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] @@ -1820,3 +2979,111 @@ sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e66 wheels = [ { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] + +[[package]] +name = "wcmatch" +version = "8.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/c4/55e0d36da61d7b8b2a49fd273e6b296fd5e8471c72ebbe438635d1af3968/wcmatch-8.5.2.tar.gz", hash = "sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2", size = 114983, upload-time = "2024-05-15T12:51:08.054Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/78/533ef890536e5ba0fd4f7df37482b5800ecaaceae9afc30978a1a7f88ff1/wcmatch-8.5.2-py3-none-any.whl", hash = "sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478", size = 39397, upload-time = "2024-05-15T12:51:06.2Z" }, +] + +[[package]] +name = "wrapt" +version = "1.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" }, + { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676, upload-time = "2025-08-12T05:51:32.636Z" }, + { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957, upload-time = "2025-08-12T05:51:54.655Z" }, + { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975, upload-time = "2025-08-12T05:52:30.109Z" }, + { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149, upload-time = "2025-08-12T05:52:09.316Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209, upload-time = "2025-08-12T05:52:10.331Z" }, + { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551, upload-time = "2025-08-12T05:52:31.137Z" }, + { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464, upload-time = "2025-08-12T05:53:01.204Z" }, + { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748, upload-time = "2025-08-12T05:53:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810, upload-time = "2025-08-12T05:52:51.906Z" }, + { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482, upload-time = "2025-08-12T05:51:45.79Z" }, + { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674, upload-time = "2025-08-12T05:51:34.629Z" }, + { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959, upload-time = "2025-08-12T05:51:56.074Z" }, + { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376, upload-time = "2025-08-12T05:52:32.134Z" }, + { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604, upload-time = "2025-08-12T05:52:11.663Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782, upload-time = "2025-08-12T05:52:12.626Z" }, + { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076, upload-time = "2025-08-12T05:52:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457, upload-time = "2025-08-12T05:53:03.936Z" }, + { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745, upload-time = "2025-08-12T05:53:02.885Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806, upload-time = "2025-08-12T05:52:53.368Z" }, + { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, + { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, + { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, + { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, + { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, + { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, + { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, + { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +] + +[[package]] +name = "zizmor" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/a8/1c0c6619e213e6e1fd8323f09661542197448db2984649d2c34097997859/zizmor-1.27.0.tar.gz", hash = "sha256:ffaf8e1bb39447e643592d669761bfbc2aa636875db9079614f6a6c81cec60c8", size = 548896, upload-time = "2026-07-14T08:16:49.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/81/ba3dbd48a5e3d56d8a9693ba3dac9484df1b3f83862a453ade9f70ef21ff/zizmor-1.27.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0de272b6c19910f5bc6965ac7d4566af26db31571e983e3b567298389d0c84c7", size = 9090700, upload-time = "2026-07-14T08:16:31.04Z" }, + { url = "https://files.pythonhosted.org/packages/2a/18/960f032faa67427afb8b3e11877b7e911d1ccff3f3bb8bec0c416d4df42c/zizmor-1.27.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cc94158472428e04298b879ef36cb94360b753ece38fa2303464dd7dae07bc68", size = 8673964, upload-time = "2026-07-14T08:16:33.038Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2a/034e4d11d1ed81e23fc9e526391616848467f0db6faeaa9e1b88fb8560ec/zizmor-1.27.0-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:b70697ebe555a28fbd3deaa259936f059f64b9f8531020f5b1f3d99087d4f62a", size = 8777649, upload-time = "2026-07-14T08:16:34.831Z" }, + { url = "https://files.pythonhosted.org/packages/2e/27/8dce2cd076ee0c39b0d9ac129703400c9c1c21c71f1709da660e3769d628/zizmor-1.27.0-py3-none-manylinux_2_28_armv7l.whl", hash = "sha256:0bba3eff43f919b04b9b6365b4ef17437649e11eafb73f603407873b65ad01dd", size = 8367119, upload-time = "2026-07-14T08:16:36.859Z" }, + { url = "https://files.pythonhosted.org/packages/21/64/4e21d26a2fc910594aaab19367ac25467a4da6438b7c381f67e90c38945e/zizmor-1.27.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:afb28123882d2b8248f1e480bf6cc6d1af102e0d3fbe22a40f7f795b1aa9d435", size = 9163603, upload-time = "2026-07-14T08:16:38.591Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/17ffd2884f8d74ce747c6ffe11481de5effb35079fd00a0725fab336e57d/zizmor-1.27.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e1042dd14fb4597a1e3007c7ef3f5a017305ca50cfacf43d603f3ae870c65eb1", size = 8807297, upload-time = "2026-07-14T08:16:40.804Z" }, + { url = "https://files.pythonhosted.org/packages/60/af/3a347da3007655c7979b4b99d6a4d309ebc344715daa7c1357f17d00d680/zizmor-1.27.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:138e65d560e3875dbbbec50eae56a9e14707f8f4e006112174e4752155c01db9", size = 8333630, upload-time = "2026-07-14T08:16:42.814Z" }, + { url = "https://files.pythonhosted.org/packages/bf/37/921c74ed022fe8e1f599d7e7353cab63d550dda68e410096ed735b7388db/zizmor-1.27.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90004342c3c5122d7e4e8e27ac6d3c462b878da0ea3d408ff45fa7d768139a76", size = 9253035, upload-time = "2026-07-14T08:16:44.354Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/9cdb03c6c2341cf273c9c34506e5193cea7e73dc50b49ccf5f93a3579bdb/zizmor-1.27.0-py3-none-win32.whl", hash = "sha256:fe8947f635c925b83ef83fcd66de5f89a012b22784bb86fcd7e2a1f4e7648c9d", size = 7538509, upload-time = "2026-07-14T08:16:46.253Z" }, + { url = "https://files.pythonhosted.org/packages/43/42/b924e569218646684d1e211f299282c0b9a0780d6b15f9e10e4a3fdaac11/zizmor-1.27.0-py3-none-win_amd64.whl", hash = "sha256:debc723721172c170d5922171a40eaebf5787a02ff3e69d30597dafdb66a21ba", size = 8643618, upload-time = "2026-07-14T08:16:47.835Z" }, +]