Compare commits

..
Author SHA1 Message Date
Vlad Stan a963b61cc8 fix: lint 2026-05-22 11:52:32 +03:00
Arc d66e22a83e Admin only toggle + remove annoying password browser thing 2026-05-21 14:18:28 +01:00
Arc 882d861f71 coming soon for sbscriptions 2026-05-21 13:56:09 +01:00
Vlad Stan 186aefad22 fix: api test 2026-05-21 14:21:49 +03:00
Vlad Stan 7badf98bb7 fix: unit tests 2026-05-21 14:12:43 +03:00
Vlad Stan 2ee098ddb1 fix: subscription_request_id 2026-05-21 14:12:43 +03:00
Vlad Stan 3723b6510d refactor: better ID check 2026-05-21 14:12:43 +03:00
Vlad Stan 03161f8e9b feat: handle subscriptions 2026-05-21 14:12:43 +03:00
Vlad Stan 5b7f9fa255 fix: remove customer_id 2026-05-21 14:12:43 +03:00
Vlad Stan 0c74adc494 feat: find customer by email 2026-05-21 14:12:43 +03:00
Vlad Stan ea6965905e fix: find customer by email 2026-05-21 14:12:43 +03:00
Vlad Stan ea8228fc84 feat: create customer id from email if missing 2026-05-21 14:12:43 +03:00
Vlad Stan 49adec6488 chore: bundle 2026-05-21 14:12:43 +03:00
ArcandVlad Stan c122bb45f4 Do the separator 🎵 2026-05-21 14:12:43 +03:00
751bd42169 feat: Adds revolut checkout and subscriptions (#3968)
Co-authored-by: alan <alan@lnbits.com>
2026-05-21 14:12:43 +03:00
Vlad Stan c4ee3a7c6b feat: introduce external_id 2026-05-21 14:12:43 +03:00
Vlad Stan 9a4e55f469 refactor: move private method to the bottom 2026-05-21 14:12:43 +03:00
Vlad Stan f45736e70e refactor: extract method 2026-05-21 14:12:43 +03:00
Vlad Stan ef39d02bd2 fix: simplify expected event types 2026-05-21 14:12:43 +03:00
Vlad Stan d66c7f7992 fix: plan 2026-05-21 14:12:43 +03:00
Vlad Stan 2abedccbc8 feat: first subscription 2026-05-21 14:12:43 +03:00
Vlad Stan cc498a7969 fix: webhook url for signature check 2026-05-21 14:12:43 +03:00
Vlad Stan cd57bf66fe fix: lint 2026-05-21 14:12:43 +03:00
Vlad Stan d921af3476 feat: basic square integration 2026-05-21 14:12:43 +03:00
66 changed files with 589 additions and 934 deletions
-2
View File
@@ -119,8 +119,6 @@ LNBITS_SITE_TAGLINE="Open Source Lightning Payments Platform"
LNBITS_SITE_DESCRIPTION="The world's most powerful suite of bitcoin tools. Run for yourself, for others, or as part of a stack." LNBITS_SITE_DESCRIPTION="The world's most powerful suite of bitcoin tools. Run for yourself, for others, or as part of a stack."
# Choose from bitcoin, mint, flamingo, freedom, salvador, autumn, monochrome, classic, cyber # Choose from bitcoin, mint, flamingo, freedom, salvador, autumn, monochrome, classic, cyber
LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador, cyber" LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador, cyber"
# Toggle the background styling on burger menus / drawers
# LNBITS_DEFAULT_BURGER_MENU_BACKGROUND=true
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg" # LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
###################################### ######################################
+5 -4
View File
@@ -306,7 +306,7 @@ async def update_payment_checking_id(
await (conn or db).execute( await (conn or db).execute(
f""" f"""
UPDATE apipayments UPDATE apipayments
SET checking_id = :new_id, updated_at = {db.timestamp_placeholder("now")} SET checking_id = :new_id, updated_at = {db.timestamp_placeholder('now')}
WHERE checking_id = :old_id WHERE checking_id = :old_id
""", # noqa: S608 """, # noqa: S608
{ {
@@ -321,15 +321,13 @@ async def update_payment(
payment: Payment, payment: Payment,
new_checking_id: str | None = None, new_checking_id: str | None = None,
conn: Connection | None = None, conn: Connection | None = None,
) -> Payment: ) -> None:
payment.updated_at = datetime.now(timezone.utc) payment.updated_at = datetime.now(timezone.utc)
await (conn or db).update( await (conn or db).update(
"apipayments", payment, "WHERE checking_id = :checking_id" "apipayments", payment, "WHERE checking_id = :checking_id"
) )
if new_checking_id and new_checking_id != payment.checking_id: if new_checking_id and new_checking_id != payment.checking_id:
await update_payment_checking_id(payment.checking_id, new_checking_id, conn) await update_payment_checking_id(payment.checking_id, new_checking_id, conn)
payment.checking_id = new_checking_id
return payment
async def get_payments_history( async def get_payments_history(
@@ -401,6 +399,7 @@ async def get_payment_count_stats(
user_id: str | None = None, user_id: str | None = None,
conn: Connection | None = None, conn: Connection | None = None,
) -> list[PaymentCountStat]: ) -> list[PaymentCountStat]:
if not filters: if not filters:
filters = Filters() filters = Filters()
extra_stmts = [] extra_stmts = []
@@ -433,6 +432,7 @@ async def get_daily_stats(
user_id: str | None = None, user_id: str | None = None,
conn: Connection | None = None, conn: Connection | None = None,
) -> tuple[list[PaymentDailyStats], list[PaymentDailyStats]]: ) -> tuple[list[PaymentDailyStats], list[PaymentDailyStats]]:
if not filters: if not filters:
filters = Filters() filters = Filters()
@@ -482,6 +482,7 @@ async def get_wallets_stats(
user_id: str | None = None, user_id: str | None = None,
conn: Connection | None = None, conn: Connection | None = None,
) -> list[PaymentWalletStats]: ) -> list[PaymentWalletStats]:
if not filters: if not filters:
filters = Filters() filters = Filters()
-2
View File
@@ -25,7 +25,6 @@ from .payments import (
PaymentState, PaymentState,
PaymentWalletStats, PaymentWalletStats,
SettleInvoice, SettleInvoice,
UpdatePaymentExtra,
) )
from .tinyurl import TinyURL from .tinyurl import TinyURL
from .users import ( from .users import (
@@ -91,7 +90,6 @@ __all__ = [
"SimpleStatus", "SimpleStatus",
"TinyURL", "TinyURL",
"UpdateBalance", "UpdateBalance",
"UpdatePaymentExtra",
"UpdateSuperuserPassword", "UpdateSuperuserPassword",
"UpdateUser", "UpdateUser",
"UpdateUserPassword", "UpdateUserPassword",
-5
View File
@@ -35,11 +35,6 @@ class PaymentExtra(BaseModel):
lnurl_response: str | None = None lnurl_response: str | None = None
class UpdatePaymentExtra(BaseModel):
payment_hash: str
extra: dict = Field(default_factory=dict)
class PayInvoice(BaseModel): class PayInvoice(BaseModel):
payment_request: str payment_request: str
description: str | None = None description: str | None = None
+11 -8
View File
@@ -133,6 +133,10 @@ async def create_fiat_invoice(
raise ValueError( raise ValueError(
f"Fiat provider '{fiat_provider_name}' is not enabled.", f"Fiat provider '{fiat_provider_name}' is not enabled.",
) )
if settings.fiat_providers_admin_only:
wallet = await get_wallet(wallet_id, conn=conn)
if not wallet or not settings.is_admin_user(wallet.user):
raise ValueError("Fiat providers are available to admins only.")
if invoice_data.unit == "sat": if invoice_data.unit == "sat":
raise ValueError("Fiat provider cannot be used with satoshis.") raise ValueError("Fiat provider cannot be used with satoshis.")
@@ -171,15 +175,15 @@ async def create_fiat_invoice(
internal_payment.fiat_provider = fiat_provider_name internal_payment.fiat_provider = fiat_provider_name
internal_payment.extra["fiat_checking_id"] = fiat_invoice.checking_id internal_payment.extra["fiat_checking_id"] = fiat_invoice.checking_id
# TODO: move to payment # todo: move to payent
internal_payment.extra["fiat_payment_request"] = fiat_invoice.payment_request internal_payment.extra["fiat_payment_request"] = fiat_invoice.payment_request
new_checking_id = ( new_checking_id = (
f"fiat_{fiat_provider_name}_" f"fiat_{fiat_provider_name}_"
f"{fiat_invoice.checking_id or internal_payment.checking_id}" f"{fiat_invoice.checking_id or internal_payment.checking_id}"
) )
internal_payment = await update_payment( await update_payment(internal_payment, new_checking_id, conn=conn)
internal_payment, new_checking_id, conn=conn internal_payment.checking_id = new_checking_id
)
return internal_payment return internal_payment
@@ -374,7 +378,7 @@ async def update_pending_payment(
status = await check_payment_status(payment) status = await check_payment_status(payment)
if status.failed: if status.failed:
payment.status = PaymentState.FAILED payment.status = PaymentState.FAILED
payment = await update_payment(payment, conn=conn) await update_payment(payment, conn=conn)
elif status.success: elif status.success:
payment = await update_payment_success_status(payment, status, conn=conn) payment = await update_payment_success_status(payment, status, conn=conn)
return payment return payment
@@ -876,7 +880,7 @@ async def update_payment_success_status(
payment.status = PaymentState.SUCCESS payment.status = PaymentState.SUCCESS
payment.fee = -(abs(status.fee_msat or 0) + abs(service_fee_msat)) payment.fee = -(abs(status.fee_msat or 0) + abs(service_fee_msat))
payment.preimage = payment.preimage or status.preimage payment.preimage = payment.preimage or status.preimage
payment = await update_payment(payment, conn=conn) await update_payment(payment, conn=conn)
return payment return payment
@@ -1099,9 +1103,8 @@ async def update_invoice_callback(checking_id: str) -> Payment | None:
payment.fee = status.fee_msat or payment.fee payment.fee = status.fee_msat or payment.fee
# only overwrite preimage if status.preimage provides it # only overwrite preimage if status.preimage provides it
payment.preimage = status.preimage or payment.preimage payment.preimage = status.preimage or payment.preimage
payment.status = PaymentState.SUCCESS payment.status = PaymentState.SUCCESS
payment = await update_payment(payment) await update_payment(payment)
if payment.fiat_provider: if payment.fiat_provider:
await handle_fiat_payment_confirmation(payment) await handle_fiat_payment_confirmation(payment)
return payment return payment
+1 -7
View File
@@ -36,7 +36,6 @@ from lnbits.decorators import (
check_account_exists, check_account_exists,
check_admin, check_admin,
check_user_exists, check_user_exists,
optional_user_id,
) )
from lnbits.helpers import ( from lnbits.helpers import (
create_access_token, create_access_token,
@@ -321,10 +320,7 @@ async def api_delete_user_api_token(
@auth_router.get("/{provider}", description="SSO Provider") @auth_router.get("/{provider}", description="SSO Provider")
async def login_with_sso_provider( async def login_with_sso_provider(
request: Request, request: Request, provider: str, user_id: str | None = None
provider: str,
user_id: str | None,
auth_user_id: str | None = Depends(optional_user_id),
): ):
provider_sso = _new_sso(provider) provider_sso = _new_sso(provider)
if not provider_sso: if not provider_sso:
@@ -332,8 +328,6 @@ async def login_with_sso_provider(
HTTPStatus.FORBIDDEN, HTTPStatus.FORBIDDEN,
f"Login by '{provider}' not allowed.", f"Login by '{provider}' not allowed.",
) )
if user_id and user_id != auth_user_id:
raise HTTPException(HTTPStatus.FORBIDDEN, "User ID mismatch.")
provider_sso.redirect_uri = str(request.base_url) + f"api/v1/auth/{provider}/token" provider_sso.redirect_uri = str(request.base_url) + f"api/v1/auth/{provider}/token"
with provider_sso: with provider_sso:
+33 -29
View File
@@ -357,20 +357,16 @@ async def handle_revolut_event(event: dict):
return return
payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}") payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}")
if payment: if not payment:
await check_fiat_status(payment)
return
if event_type == "ORDER_COMPLETED":
logger.warning(f"No payment found for Revolut order: '{order_id}'.") logger.warning(f"No payment found for Revolut order: '{order_id}'.")
await _handle_revolut_subscription_order_paid(order_id) await _handle_revolut_subscription_order_paid(order_id)
return return
logger.info(f"Ignoring Revolut authorised order without payment: '{order_id}'.") await check_fiat_status(payment)
return return
if event_type == "SUBSCRIPTION_INITIATED": if event_type == "SUBSCRIPTION_INITIATED":
logger.info("Revolut subscription initiated event received.") await _handle_revolut_subscription_initiated(event)
return return
if event_type in [ if event_type in [
@@ -384,6 +380,23 @@ async def handle_revolut_event(event: dict):
logger.warning(f"Unhandled Revolut event type: '{event_type}'.") logger.warning(f"Unhandled Revolut event type: '{event_type}'.")
async def _handle_revolut_subscription_initiated(event: dict):
subscription_id = event.get("subscription_id")
if not subscription_id:
subscription_id = event.get("id")
if not subscription_id:
logger.warning("Revolut subscription event missing subscription_id.")
return
fiat_provider = await _get_revolut_provider()
if not fiat_provider:
return
subscription = await fiat_provider.get_subscription(subscription_id)
await _handle_revolut_subscription(subscription, fiat_provider)
async def _get_revolut_provider() -> RevolutWallet | None: async def _get_revolut_provider() -> RevolutWallet | None:
fiat_provider = await get_fiat_provider("revolut") fiat_provider = await get_fiat_provider("revolut")
if not isinstance(fiat_provider, RevolutWallet): if not isinstance(fiat_provider, RevolutWallet):
@@ -393,10 +406,7 @@ async def _get_revolut_provider() -> RevolutWallet | None:
async def _handle_revolut_subscription( async def _handle_revolut_subscription(
subscription: dict, subscription: dict, fiat_provider: RevolutWallet
fiat_provider: RevolutWallet,
order_id: str | None = None,
order: dict | None = None,
): ):
subscription_id = subscription.get("id") subscription_id = subscription.get("id")
if not subscription_id: if not subscription_id:
@@ -410,17 +420,16 @@ async def _handle_revolut_subscription(
logger.warning("Revolut subscription event missing LNbits metadata.") logger.warning("Revolut subscription event missing LNbits metadata.")
return return
if not order_id: cycle_id = subscription.get("current_cycle_id")
cycle_id = subscription.get("current_cycle_id") if not cycle_id:
if not cycle_id: logger.warning("Revolut subscription missing current_cycle_id.")
logger.warning("Revolut subscription missing current_cycle_id.") return
return
cycle = await fiat_provider.get_subscription_cycle(subscription_id, cycle_id) cycle = await fiat_provider.get_subscription_cycle(subscription_id, cycle_id)
order_id = cycle.get("order_id") order_id = cycle.get("order_id")
if not order_id: if not order_id:
logger.warning("Revolut subscription cycle missing order_id.") logger.warning("Revolut subscription cycle missing order_id.")
return return
existing_payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}") existing_payment = await get_standalone_payment(f"fiat_revolut_order_{order_id}")
if existing_payment: if existing_payment:
@@ -430,8 +439,7 @@ async def _handle_revolut_subscription(
await check_fiat_status(existing_payment) await check_fiat_status(existing_payment)
return return
if not order: order = await fiat_provider.get_order(order_id)
order = await fiat_provider.get_order(order_id)
amount_minor = order.get("amount") amount_minor = order.get("amount")
currency = (order.get("currency") or "").upper() currency = (order.get("currency") or "").upper()
if amount_minor is None or not currency: if amount_minor is None or not currency:
@@ -467,9 +475,7 @@ async def _handle_revolut_subscription_order_paid(order_id: str):
return return
order = await fiat_provider.get_order(order_id) order = await fiat_provider.get_order(order_id)
order_type = (order.get("type") or "").lower() if order.get("type") != "payment" or order.get("state") != "completed":
order_state = (order.get("state") or "").upper()
if order_type != "payment" or order_state != "COMPLETED":
logger.warning(f"Revolut order is not a completed payment: '{order_id}'.") logger.warning(f"Revolut order is not a completed payment: '{order_id}'.")
return return
@@ -484,9 +490,7 @@ async def _handle_revolut_subscription_order_paid(order_id: str):
logger.warning(f"Revolut subscription is not active: '{subscription_id}'.") logger.warning(f"Revolut subscription is not active: '{subscription_id}'.")
return return
await _handle_revolut_subscription( await _handle_revolut_subscription_initiated(subscription)
subscription, fiat_provider, order_id=order_id, order=order
)
async def _create_revolut_subscription_payment( async def _create_revolut_subscription_payment(
+1 -1
View File
@@ -32,7 +32,7 @@ generic_router = APIRouter(
@generic_router.get("/favicon.ico", response_class=FileResponse) @generic_router.get("/favicon.ico", response_class=FileResponse)
async def favicon(): async def favicon():
return RedirectResponse(settings.root_path + settings.lnbits_qr_logo) return RedirectResponse(settings.lnbits_qr_logo)
@generic_router.get("/robots.txt", response_class=HTMLResponse) @generic_router.get("/robots.txt", response_class=HTMLResponse)
-33
View File
@@ -34,7 +34,6 @@ from lnbits.core.models import (
PaymentWalletStats, PaymentWalletStats,
SettleInvoice, SettleInvoice,
SimpleStatus, SimpleStatus,
UpdatePaymentExtra,
) )
from lnbits.core.models.payments import UpdatePaymentLabels from lnbits.core.models.payments import UpdatePaymentLabels
from lnbits.core.models.users import AccountId from lnbits.core.models.users import AccountId
@@ -298,38 +297,6 @@ async def api_update_payment_labels(
return SimpleStatus(success=True, message="Payment labels updated.") return SimpleStatus(success=True, message="Payment labels updated.")
@payment_router.patch(
"/extra",
name="Update payment extra",
description="Append new extra metadata to a payment.",
response_model=Payment,
)
async def api_update_payment_extra(
data: UpdatePaymentExtra,
key_type: WalletTypeInfo = Depends(require_admin_key),
) -> Payment:
payment = await get_standalone_payment(
data.payment_hash, wallet_id=key_type.wallet.id
)
if payment is None:
raise HTTPException(HTTPStatus.NOT_FOUND, "Payment does not exist.")
if not payment.success:
raise HTTPException(
HTTPStatus.BAD_REQUEST, "Payment extra can only be updated after success."
)
duplicate_keys = sorted(set(payment.extra).intersection(data.extra))
if duplicate_keys:
raise HTTPException(
HTTPStatus.BAD_REQUEST,
f"Extra keys already exist: {', '.join(duplicate_keys)}.",
)
payment.extra.update(data.extra)
await update_payment(payment)
return payment
@payment_router.get("/fee-reserve") @payment_router.get("/fee-reserve")
async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONResponse: async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONResponse:
invoice_obj = bolt11.decode(invoice) invoice_obj = bolt11.decode(invoice)
-2
View File
@@ -517,7 +517,6 @@ async def _check_account_api_access(
raise HTTPException(HTTPStatus.FORBIDDEN, "Method not allowed.") raise HTTPException(HTTPStatus.FORBIDDEN, "Method not allowed.")
# TODO: this messes up my extension urls
def url_for_interceptor(original_method): def url_for_interceptor(original_method):
def normalize_url(self, *args, **kwargs): def normalize_url(self, *args, **kwargs):
url = original_method(self, *args, **kwargs) url = original_method(self, *args, **kwargs)
@@ -528,7 +527,6 @@ def url_for_interceptor(original_method):
# Upgraded extensions modify the path. # Upgraded extensions modify the path.
# This interceptor ensures that the path is normalized. # This interceptor ensures that the path is normalized.
# TODO: this messes up my extension urls
Request.url_for = url_for_interceptor(Request.url_for) # type: ignore[method-assign] Request.url_for = url_for_interceptor(Request.url_for) # type: ignore[method-assign]
+3 -3
View File
@@ -131,15 +131,15 @@ class FiatSubscriptionResponse(BaseModel):
class FiatPaymentSuccessStatus(FiatPaymentStatus): class FiatPaymentSuccessStatus(FiatPaymentStatus):
paid = True # type: ignore[reportIncompatibleVariableOverride] paid = True
class FiatPaymentFailedStatus(FiatPaymentStatus): class FiatPaymentFailedStatus(FiatPaymentStatus):
paid = False # type: ignore[reportIncompatibleVariableOverride] paid = False
class FiatPaymentPendingStatus(FiatPaymentStatus): class FiatPaymentPendingStatus(FiatPaymentStatus):
paid = None # type: ignore[reportIncompatibleVariableOverride] paid = None
class FiatProvider(ABC): class FiatProvider(ABC):
+4 -17
View File
@@ -48,11 +48,8 @@ def url_for(endpoint: str, external: bool | None = False, **params: Any) -> str:
return url return url
def static_url_for(static: str, path: str, no_cache: bool = False) -> str: def static_url_for(static: str, path: str) -> str:
url = f"{settings.root_path}{static}/{path}" return f"/{static}/{path}?v={settings.server_startup_time}"
if no_cache:
url += f"?v={settings.server_startup_time}"
return url
def template_renderer(additional_folders: list | None = None) -> Jinja2Templates: def template_renderer(additional_folders: list | None = None) -> Jinja2Templates:
@@ -60,6 +57,7 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
"lnbits/templates", "lnbits/templates",
settings.extension_builder_working_dir_path.as_posix(), settings.extension_builder_working_dir_path.as_posix(),
] ]
if additional_folders: if additional_folders:
additional_folders += [ additional_folders += [
Path(settings.lnbits_extensions_path, "extensions", f) Path(settings.lnbits_extensions_path, "extensions", f)
@@ -71,7 +69,6 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
t.env.globals["normalize_path"] = normalize_path t.env.globals["normalize_path"] = normalize_path
# used in base.html # used in base.html
t.env.globals["ROOT_PATH"] = settings.root_path
t.env.globals["SITE_TITLE"] = settings.lnbits_site_title t.env.globals["SITE_TITLE"] = settings.lnbits_site_title
t.env.globals["LNBITS_APPLE_TOUCH_ICON"] = settings.lnbits_apple_touch_icon t.env.globals["LNBITS_APPLE_TOUCH_ICON"] = settings.lnbits_apple_touch_icon
t.env.globals["SETTINGS"] = settings.to_public().dict(by_alias=True) t.env.globals["SETTINGS"] = settings.to_public().dict(by_alias=True)
@@ -313,8 +310,6 @@ def get_api_routes(routes: list) -> dict[str, str]:
def path_segments(path: str) -> list[str]: def path_segments(path: str) -> list[str]:
path = path.strip("/") path = path.strip("/")
# Remove empty segments caused by '//' in the path
# segments = [s for s in path.split("/") if s]
segments = path.split("/") segments = path.split("/")
if len(segments) < 2: if len(segments) < 2:
return segments return segments
@@ -324,16 +319,8 @@ def path_segments(path: str) -> list[str]:
def normalize_path(path: str | None) -> str: def normalize_path(path: str | None) -> str:
print(path)
path = path or "" path = path or ""
segments = path_segments(path) return "/" + "/".join(path_segments(path))
print(segments)
joined = "/".join(segments)
print("!!!!!!!!!!")
print(joined)
return joined
# return "/" + "/".join(path_segments(path))
def normalize_endpoint(endpoint: str, add_proto=True) -> str: def normalize_endpoint(endpoint: str, add_proto=True) -> str:
+1 -3
View File
@@ -20,7 +20,6 @@ from lnbits.helpers import normalize_path, template_renderer
from lnbits.settings import settings from lnbits.settings import settings
# TODO: root path should be considered here?
class InstalledExtensionMiddleware: class InstalledExtensionMiddleware:
# This middleware class intercepts calls made to the extensions API and: # This middleware class intercepts calls made to the extensions API and:
# - it blocks the calls if the extension has been disabled or uninstalled. # - it blocks the calls if the extension has been disabled or uninstalled.
@@ -52,7 +51,7 @@ class InstalledExtensionMiddleware:
await self.app(scope, receive, send) await self.app(scope, receive, send)
return return
# re-route all traffic if the extension has been upgraded # re-route all trafic if the extension has been upgraded
if top_path in settings.lnbits_upgraded_extensions: if top_path in settings.lnbits_upgraded_extensions:
upgrade_path = ( upgrade_path = (
f"""{settings.lnbits_upgraded_extensions[top_path]}/{top_path}""" f"""{settings.lnbits_upgraded_extensions[top_path]}/{top_path}"""
@@ -242,7 +241,6 @@ def add_first_install_middleware(app: FastAPI):
async def first_install_middleware(request: Request, call_next): async def first_install_middleware(request: Request, call_next):
if ( if (
settings.first_install settings.first_install
# TODO: root path should be considered here?
and request.url.path != "/api/v1/auth/first_install" and request.url.path != "/api/v1/auth/first_install"
and request.url.path != "/first_install" and request.url.path != "/first_install"
and not request.url.path.startswith("/static") and not request.url.path.startswith("/static")
+1 -13
View File
@@ -17,11 +17,6 @@ from lnbits.settings import set_cli_settings, settings
) )
@click.option("--port", default=settings.port, help="Port to listen on") @click.option("--port", default=settings.port, help="Port to listen on")
@click.option("--host", default=settings.host, help="Host to run LNbits on") @click.option("--host", default=settings.host, help="Host to run LNbits on")
@click.option(
"--root-path",
default=settings.root_path,
help="Root path of proxy, my.lnbits.com/rootpath ",
)
@click.option( @click.option(
"--forwarded-allow-ips", "--forwarded-allow-ips",
default=settings.forwarded_allow_ips, default=settings.forwarded_allow_ips,
@@ -35,7 +30,6 @@ from lnbits.settings import set_cli_settings, settings
def main( def main(
port: int, port: int,
host: str, host: str,
root_path: str,
forwarded_allow_ips: str, forwarded_allow_ips: str,
ssl_keyfile: str, ssl_keyfile: str,
ssl_certfile: str, ssl_certfile: str,
@@ -52,12 +46,7 @@ def main(
parents=True, exist_ok=True parents=True, exist_ok=True
) )
set_cli_settings( set_cli_settings(host=host, port=port, forwarded_allow_ips=forwarded_allow_ips)
host=host,
port=port,
forwarded_allow_ips=forwarded_allow_ips,
root_path=root_path,
)
while True: while True:
config = uvicorn.Config( config = uvicorn.Config(
@@ -65,7 +54,6 @@ def main(
loop="uvloop", loop="uvloop",
port=port, port=port,
host=host, host=host,
root_path=root_path,
forwarded_allow_ips=forwarded_allow_ips, forwarded_allow_ips=forwarded_allow_ips,
ssl_keyfile=ssl_keyfile, ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile, ssl_certfile=ssl_certfile,
+14 -7
View File
@@ -300,7 +300,6 @@ class ThemesSettings(LNbitsSettings):
lnbits_default_card_rounded: bool = Field(default=True) lnbits_default_card_rounded: bool = Field(default=True)
lnbits_default_card_gradient: bool = Field(default=True) lnbits_default_card_gradient: bool = Field(default=True)
lnbits_default_card_shadow: bool = Field(default=False) lnbits_default_card_shadow: bool = Field(default=False)
lnbits_default_burger_menu_background: bool = Field(default=True)
class OpsSettings(LNbitsSettings): class OpsSettings(LNbitsSettings):
@@ -771,6 +770,8 @@ class FiatProvidersSettings(
SquareFiatProvider, SquareFiatProvider,
RevolutFiatProvider, RevolutFiatProvider,
): ):
fiat_providers_admin_only: bool = Field(default=True)
def is_fiat_provider_enabled(self, provider: str | None) -> bool: def is_fiat_provider_enabled(self, provider: str | None) -> bool:
""" """
Checks if a specific fiat provider is enabled. Checks if a specific fiat provider is enabled.
@@ -791,6 +792,15 @@ class FiatProvidersSettings(
""" """
Returns a list of fiat payment methods allowed for the user. Returns a list of fiat payment methods allowed for the user.
""" """
if self.fiat_providers_admin_only:
if not self.is_admin_user(user_id):
return []
return [
provider
for provider in ["stripe", "paypal", "square", "revolut"]
if self.is_fiat_provider_enabled(provider)
]
allowed_providers = [] allowed_providers = []
if self.stripe_enabled and ( if self.stripe_enabled and (
not self.stripe_limits.allowed_users not self.stripe_limits.allowed_users
@@ -1047,7 +1057,7 @@ class EditableSettings(
class UpdateSettings(EditableSettings): class UpdateSettings(EditableSettings):
class Config(EditableSettings.Config): class Config:
extra = Extra.forbid extra = Extra.forbid
@@ -1060,7 +1070,6 @@ class EnvSettings(LNbitsSettings):
auth_https_only: bool = Field(default=True) auth_https_only: bool = Field(default=True)
host: str = Field(default="127.0.0.1") host: str = Field(default="127.0.0.1")
port: int = Field(default=5000, gt=0) port: int = Field(default=5000, gt=0)
root_path: str = Field(default="/")
forwarded_allow_ips: str = Field(default="*") forwarded_allow_ips: str = Field(default="*")
lnbits_title: str = Field(default="LNbits API") lnbits_title: str = Field(default="LNbits API")
lnbits_path: str = Field(default=".") lnbits_path: str = Field(default=".")
@@ -1199,11 +1208,11 @@ class ReadOnlySettings(
class Settings(EditableSettings, ReadOnlySettings, TransientSettings, BaseSettings): class Settings(EditableSettings, ReadOnlySettings, TransientSettings, BaseSettings):
class Config(EditableSettings.Config, BaseSettings.Config): # type: ignore[misc] class Config:
env_file = ".env" env_file = ".env"
env_file_encoding = "utf-8" env_file_encoding = "utf-8"
case_sensitive = False case_sensitive = False
json_loads = list_parse_fallback # type: ignore[assignment] json_loads = list_parse_fallback
def is_user_allowed(self, user_id: str) -> bool: def is_user_allowed(self, user_id: str) -> bool:
return ( return (
@@ -1283,7 +1292,6 @@ class PublicSettings(BaseModel):
default_card_rounded: bool = Field(alias="defaultCardRounded") default_card_rounded: bool = Field(alias="defaultCardRounded")
default_card_gradient: bool = Field(alias="defaultCardGradient") default_card_gradient: bool = Field(alias="defaultCardGradient")
default_card_shadow: bool = Field(alias="defaultCardShadow") default_card_shadow: bool = Field(alias="defaultCardShadow")
default_burger_menu_background: bool = Field(alias="defaultBurgerMenuBackground")
denomination: str | None = Field() denomination: str | None = Field()
extensions: list[str] = Field() extensions: list[str] = Field()
allowed_currencies: list[str] = Field(alias="allowedCurrencies") allowed_currencies: list[str] = Field(alias="allowedCurrencies")
@@ -1347,7 +1355,6 @@ class PublicSettings(BaseModel):
defaultCardRounded=settings.lnbits_default_card_rounded, defaultCardRounded=settings.lnbits_default_card_rounded,
defaultCardGradient=settings.lnbits_default_card_gradient, defaultCardGradient=settings.lnbits_default_card_gradient,
defaultCardShadow=settings.lnbits_default_card_shadow, defaultCardShadow=settings.lnbits_default_card_shadow,
defaultBurgerMenuBackground=settings.lnbits_default_burger_menu_background,
denomination=settings.lnbits_denomination, denomination=settings.lnbits_denomination,
extensions=list(settings.lnbits_installed_extensions_ids), extensions=list(settings.lnbits_installed_extensions_ids),
allowedCurrencies=settings.lnbits_allowed_currencies, allowedCurrencies=settings.lnbits_allowed_currencies,
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -14
View File
@@ -212,15 +212,12 @@ body.bg-image .q-page-container {
backdrop-filter: none; /* Ensure the page content is not affected */ backdrop-filter: none; /* Ensure the page content is not affected */
} }
body.body--dark .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { body.body--dark .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark),
--q-dark: rgba(29, 29, 29, 0.3);
background-color: var(--q-dark);
}
body.body--dark .q-header, body.body--dark .q-header,
body.body--dark .q-drawer { body.body--dark .q-drawer {
--q-dark: rgba(29, 29, 29, 0.3); --q-dark: rgba(29, 29, 29, 0.3);
background-color: var(--q-dark); background-color: var(--q-dark);
backdrop-filter: brightness(0.8); backdrop-filter: blur(6px) brightness(0.8);
} }
body.rounded-ui .q-card, body.rounded-ui .q-card,
@@ -391,18 +388,11 @@ body[data-theme=salvador].card-gradient.body--dark .q-drawer {
} }
body.card-shadow .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { body.card-shadow .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) {
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18); filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.18));
} }
body.card-shadow.body--dark .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { body.card-shadow.body--dark .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) {
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45); filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
}
body.no-burger-background .q-drawer {
background-color: transparent !important;
background-image: none !important;
backdrop-filter: none !important;
box-shadow: none !important;
} }
:root { :root {
-2
View File
@@ -490,8 +490,6 @@ window.localisation.en = {
toggle_card_gradient: 'Toggle gradient on cards', toggle_card_gradient: 'Toggle gradient on cards',
card_shadow: 'Card Shadow', card_shadow: 'Card Shadow',
toggle_card_shadow: 'Toggle shadow on cards', toggle_card_shadow: 'Toggle shadow on cards',
burger_menu_background: 'Burger Menu Background',
toggle_burger_menu_background: 'Toggle burger menu background',
language: 'Language', language: 'Language',
assets: 'Assets', assets: 'Assets',
max_asset_size_mb: 'Max Asset Size (MB)', max_asset_size_mb: 'Max Asset Size (MB)',
+51 -19
View File
@@ -1,7 +1,5 @@
window._lnbitsApi = { window._lnbitsApi = {
request(method, url, apiKey, data, options = {}) { request(method, url, apiKey, data, options = {}) {
url = ROOT_PATH + url.replace(/^\/+/, '') // Ensure single slash after rootPath
console.log(`API Request: ${method.toUpperCase()} ${url}`)
return axios({ return axios({
method: method, method: method,
url: url, url: url,
@@ -69,41 +67,75 @@ window._lnbitsApi = {
}) })
}, },
register(username, email, password, password_repeat, invitation_code) { register(username, email, password, password_repeat, invitation_code) {
return this.request('post', '/api/v1/auth/register', null, { return axios({
username, method: 'POST',
email, url: '/api/v1/auth/register',
password, data: {
password_repeat, username,
invitation_code email,
password,
password_repeat,
invitation_code
}
}) })
}, },
reset(reset_key, password, password_repeat) { reset(reset_key, password, password_repeat) {
return this.request('put', '/api/v1/auth/reset', null, { return axios({
reset_key, method: 'PUT',
password, url: '/api/v1/auth/reset',
password_repeat data: {
reset_key,
password,
password_repeat
}
}) })
}, },
getAuthUser() { getAuthUser() {
return this.request('get', '/api/v1/auth') return axios({
method: 'GET',
url: '/api/v1/auth'
})
}, },
login(username, password) { login(username, password) {
return this.request('post', '/api/v1/auth', null, {username, password}) return axios({
method: 'POST',
url: '/api/v1/auth',
data: {username, password}
})
}, },
loginByProvider(provider, headers, data) { loginByProvider(provider, headers, data) {
return this.request('post', `/api/v1/auth/${provider}`, null, data) return axios({
method: 'POST',
url: `/api/v1/auth/${provider}`,
headers: headers,
data
})
}, },
loginUsr(usr) { loginUsr(usr) {
return this.request('post', '/api/v1/auth/usr', null, {usr}) return axios({
method: 'POST',
url: '/api/v1/auth/usr',
data: {usr}
})
}, },
logout() { logout() {
return this.request('post', '/api/v1/auth/logout') return axios({
method: 'POST',
url: '/api/v1/auth/logout'
})
}, },
impersonateUser(usr) { impersonateUser(usr) {
return this.request('POST', '/api/v1/auth/impersonate', null, {usr}) return axios({
method: 'POST',
url: '/api/v1/auth/impersonate',
data: {usr}
})
}, },
stopImpersonation() { stopImpersonation() {
return this.request('DELETE', '/api/v1/auth/impersonate') return axios({
method: 'DELETE',
url: '/api/v1/auth/impersonate'
})
}, },
getAuthenticatedUser() { getAuthenticatedUser() {
return this.request('get', '/api/v1/auth') return this.request('get', '/api/v1/auth')
+1 -3
View File
@@ -452,9 +452,7 @@ window.app.component('username-password', {
confirmationMethod: 'code', confirmationMethod: 'code',
confirmationEmail: '', confirmationEmail: '',
confirmationCode: this.invitationCode || '', confirmationCode: this.invitationCode || '',
showConfirmationCode: false, showConfirmationCode: false
showPwd: false,
showPwdRepeat: false
} }
}, },
methods: { methods: {
@@ -12,6 +12,21 @@ window.app.component('lnbits-admin-fiat-providers', {
} }
}, },
computed: { computed: {
fiatProvidersAllUsers: {
get() {
return this.formData?.fiat_providers_admin_only === false
},
set(value) {
this.formData.fiat_providers_admin_only = !value
this.formData.touch = null
}
},
fiatProviderAccessLabel() {
return this.fiatProvidersAllUsers ? 'All users' : 'Admins only'
},
secretInputStyle() {
return this.hideInputToggle ? {'-webkit-text-security': 'disc'} : {}
},
stripeWebhookUrl() { stripeWebhookUrl() {
return ( return (
this.formData?.stripe_payment_webhook_url || this.formData?.stripe_payment_webhook_url ||
@@ -360,37 +360,18 @@ window.app.component('lnbits-payment-list', {
paymentTableRowKey(row) { paymentTableRowKey(row) {
return row.payment_hash + row.amount return row.payment_hash + row.amount
}, },
async exportCSV(detailed = false) { exportCSV(detailed = false) {
// status is important for export but it is not in paymentsTable // status is important for export but it is not in paymentsTable
// because it is manually added with payment detail link and icons // because it is manually added with payment detail link and icons
// and would cause duplication in the list // and would cause duplication in the list
const pagination = this.paymentsTable.pagination const pagination = this.paymentsTable.pagination
const maxPages = 100 const query = {
const limit = 1000 sortby: pagination.sortBy ?? 'time',
let payments = [] direction: pagination.descending ? 'desc' : 'asc'
}
this.paymentsCSV.loading = true const params = new URLSearchParams(query)
try { LNbits.api.getPayments(this.wallet, params).then(response => {
for (let page = 0; page < maxPages; page++) { let payments = response.data.data.map(this.mapPayment)
const query = {
sortby: pagination.sortBy ?? 'time',
direction: pagination.descending ? 'desc' : 'asc',
limit,
offset: page * limit
}
const params = new URLSearchParams(query)
const response = await LNbits.api.getPayments(this.wallet, params)
const pagePayments = response.data.data || []
payments = payments.concat(pagePayments.map(this.mapPayment))
if (
pagePayments.length < limit ||
payments.length >= response.data.total
) {
break
}
}
let columns = this.paymentsCSV.columns let columns = this.paymentsCSV.columns
if (detailed) { if (detailed) {
@@ -419,11 +400,7 @@ window.app.component('lnbits-payment-list', {
payments, payments,
this.wallet.name + '-payments' this.wallet.name + '-payments'
) )
} catch (err) { })
LNbits.utils.notifyApiError(err)
} finally {
this.paymentsCSV.loading = false
}
}, },
addFilterTag() { addFilterTag() {
if (!this.exportTagName) return if (!this.exportTagName) return
@@ -8,10 +8,6 @@ window.app.component('lnbits-qrcode-lnurl', {
prefix: { prefix: {
type: String, type: String,
default: 'lnurlp' default: 'lnurlp'
},
href: {
type: String,
default: ''
} }
}, },
data() { data() {
@@ -25,10 +21,7 @@ window.app.component('lnbits-qrcode-lnurl', {
if (this.tab == 'bech32') { if (this.tab == 'bech32') {
const bytes = new TextEncoder().encode(this.url) const bytes = new TextEncoder().encode(this.url)
const bech32 = NostrTools.nip19.encodeBytes('lnurl', bytes) const bech32 = NostrTools.nip19.encodeBytes('lnurl', bytes)
this.lnurl = this.lnurl = `lightning:${bech32.toUpperCase()}`
this.href && this.href.trim() !== ''
? `${this.href}?lightning=${bech32.toUpperCase()}`
: `lightning:${bech32.toUpperCase()}`
} else if (this.tab == 'lud17') { } else if (this.tab == 'lud17') {
if (this.url.startsWith('http://')) { if (this.url.startsWith('http://')) {
this.lnurl = this.url.replace('http://', this.prefix + '://') this.lnurl = this.url.replace('http://', this.prefix + '://')
@@ -85,9 +85,6 @@ window.app.component('lnbits-qrcode', {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
return false return false
} else if (this.href && this.href.startsWith('http')) {
window.open(this.href, '_blank')
event.preventDefault()
} }
}, },
async writeNfcTag() { async writeNfcTag() {
@@ -69,14 +69,6 @@ window.app.component('lnbits-theme', {
document.body.classList.remove('card-shadow') document.body.classList.remove('card-shadow')
} }
}, },
'g.burgerMenuChoice'(val) {
this.$q.localStorage.set('lnbits.burgerMenu', val)
if (val === true) {
document.body.classList.remove('no-burger-background')
} else {
document.body.classList.add('no-burger-background')
}
},
'g.mobileSimple'(val) { 'g.mobileSimple'(val) {
this.$q.localStorage.set('lnbits.mobileSimple', val) this.$q.localStorage.set('lnbits.mobileSimple', val)
if (val === true) { if (val === true) {
@@ -158,9 +150,6 @@ window.app.component('lnbits-theme', {
if (this.g.cardShadowChoice === true) { if (this.g.cardShadowChoice === true) {
document.body.classList.add('card-shadow') document.body.classList.add('card-shadow')
} }
if (this.g.burgerMenuChoice !== true) {
document.body.classList.add('no-burger-background')
}
if (this.g.bgimageChoice !== '') { if (this.g.bgimageChoice !== '') {
document.body.classList.add('bg-image') document.body.classList.add('bg-image')
document.body.style.setProperty( document.body.style.setProperty(
+4 -10
View File
@@ -29,10 +29,6 @@ window.g = Vue.reactive({
SETTINGS.defaultCardGradient SETTINGS.defaultCardGradient
), ),
cardShadowChoice: localStore('lnbits.cardShadow', SETTINGS.defaultCardShadow), cardShadowChoice: localStore('lnbits.cardShadow', SETTINGS.defaultCardShadow),
burgerMenuChoice: localStore(
'lnbits.burgerMenu',
SETTINGS.defaultBurgerMenuBackground
),
reactionChoice: localStore('lnbits.reactions', SETTINGS.defaultReaction), reactionChoice: localStore('lnbits.reactions', SETTINGS.defaultReaction),
bgimageChoice: localStore( bgimageChoice: localStore(
'lnbits.backgroundImage', 'lnbits.backgroundImage',
@@ -72,7 +68,7 @@ window.dateFormat = 'YYYY-MM-DD HH:mm'
const websocketPrefix = const websocketPrefix =
window.location.protocol === 'http:' ? 'ws://' : 'wss://' window.location.protocol === 'http:' ? 'ws://' : 'wss://'
const websocketUrl = `${websocketPrefix}${window.location.host}${ROOT_PATH}api/v1/ws` const websocketUrl = `${websocketPrefix}${window.location.host}/api/v1/ws`
const _access_cookies_for_safari_refresh_do_not_delete = document.cookie const _access_cookies_for_safari_refresh_do_not_delete = document.cookie
@@ -87,11 +83,9 @@ addEventListener('online', event => {
}) })
if (navigator.serviceWorker != null) { if (navigator.serviceWorker != null) {
navigator.serviceWorker navigator.serviceWorker.register('/service-worker.js').then(registration => {
.register(ROOT_PATH + 'service-worker.js') console.log('Registered events at scope: ', registration.scope)
.then(registration => { })
console.log('Registered events at scope: ', registration.scope)
})
} }
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
+6 -19
View File
@@ -11,16 +11,11 @@ const quasarConfig = {
const DynamicComponent = { const DynamicComponent = {
async created() { async created() {
// no trailing /
const rootPath = ROOT_PATH.replace(/\/+$/, '')
const name = this.$route.path.split('/')[1] const name = this.$route.path.split('/')[1]
const path = `${rootPath}/${name}/` const path = `/${name}/`
const routesPath = `${rootPath}/${name}/static/routes.json` const routesPath = `/${name}/static/routes.json`
if (this.$router.getRoutes().some(r => r.path === path)) return if (this.$router.getRoutes().some(r => r.path === path)) return
if ( if (this.$route.fullPath.startsWith('/extensions/builder/preview')) return
this.$route.fullPath.startsWith(rootPath + '/extensions/builder/preview')
)
return
fetch(routesPath) fetch(routesPath)
.then(async res => { .then(async res => {
if (!res.ok) throw new Error('No dynamic routes found') if (!res.ok) throw new Error('No dynamic routes found')
@@ -43,17 +38,9 @@ const DynamicComponent = {
let route = RENDERED_ROUTE let route = RENDERED_ROUTE
// append trailing slash only on the root path `/path` -> `/path/` // append trailing slash only on the root path `/path` -> `/path/`
if (route.split('/').length === 2) route += '/' if (route.split('/').length === 2) route += '/'
console.log('ROUTE', route)
console.log('path / fullpath', this.$route.path, this.$route.fullPath)
if (route !== this.$route.path) { if (route !== this.$route.path) {
const rootPath = ROOT_PATH.replace(/\/+$/, '') console.log('Redirecting to non-vue route:', this.$route.fullPath)
console.log( window.location = this.$route.fullPath
'Redirecting to non-vue route:',
rootPath + this.$route.fullPath
)
// window.location = rootPath + this.$route.fullPath
return return
} }
}) })
@@ -152,7 +139,7 @@ const routes = [
] ]
window.router = VueRouter.createRouter({ window.router = VueRouter.createRouter({
history: VueRouter.createWebHistory(ROOT_PATH), history: VueRouter.createWebHistory(),
routes routes
}) })
+1 -2
View File
@@ -732,8 +732,7 @@ window.PageAccount = {
darkChoice: this.g.settings.defaultDark, darkChoice: this.g.settings.defaultDark,
cardRoundedChoice: this.g.settings.defaultCardRounded, cardRoundedChoice: this.g.settings.defaultCardRounded,
cardGradientChoice: this.g.settings.defaultCardGradient, cardGradientChoice: this.g.settings.defaultCardGradient,
cardShadowChoice: this.g.settings.defaultCardShadow, cardShadowChoice: this.g.settings.defaultCardShadow
burgerMenuChoice: this.g.settings.defaultBurgerMenuBackground
} }
this.siteCustomisationChanged(defaults) this.siteCustomisationChanged(defaults)
} }
+5 -9
View File
@@ -1,17 +1,13 @@
window._lnbitsUtils = { window._lnbitsUtils = {
urlFor(url, noCache = false) { url_for(url) {
if (url.startsWith('http://') || url.startsWith('https://')) { const _url = new URL(url, window.location.origin)
return url _url.searchParams.set('v', window.g.settings.cacheKey)
}
const rootPath = ROOT_PATH.replace(/\/+$/, '')
const _url = new URL(rootPath + url, window.location.origin)
if (!noCache) _url.searchParams.set('v', window.g.settings.cacheKey)
return _url.toString() return _url.toString()
}, },
loadScript(src) { loadScript(src) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const script = document.createElement('script') const script = document.createElement('script')
script.src = this.urlFor(src) script.src = this.url_for(src)
script.onload = () => { script.onload = () => {
resolve() resolve()
} }
@@ -22,7 +18,7 @@ window._lnbitsUtils = {
}) })
}, },
async loadTemplate(url) { async loadTemplate(url) {
return fetch(this.urlFor(url)) return fetch(this.url_for(url))
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to load template from ${url}`) throw new Error(`Failed to load template from ${url}`)
+2 -6
View File
@@ -58,15 +58,11 @@ body.bg-image {
} }
// transparent background for specific elements // transparent background for specific elements
body.body--dark { body.body--dark {
.q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark),
--q-dark: #{color.adjust(#1d1d1d, $alpha: -0.7)};
background-color: var(--q-dark);
}
.q-header, .q-header,
.q-drawer { .q-drawer {
--q-dark: #{color.adjust(#1d1d1d, $alpha: -0.7)}; --q-dark: #{color.adjust(#1d1d1d, $alpha: -0.7)};
background-color: var(--q-dark); background-color: var(--q-dark);
backdrop-filter: brightness(0.8); backdrop-filter: blur(6px) brightness(0.8);
} }
} }
+2 -11
View File
@@ -61,21 +61,12 @@ body.rounded-ui {
body.card-shadow { body.card-shadow {
.q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) {
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18); filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.18));
} }
} }
body.card-shadow.body--dark { body.card-shadow.body--dark {
.q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) { .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark) {
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45); filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
}
}
body.no-burger-background {
.q-drawer {
background-color: transparent !important;
background-image: none !important;
backdrop-filter: none !important;
box-shadow: none !important;
} }
} }
+3 -9
View File
@@ -23,7 +23,6 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no"
/> />
<link rel="icon" type="image/x-icon" href="{{ ROOT_PATH }}favicon.ico" />
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<link <link
@@ -33,13 +32,6 @@
{% if web_manifest %} {% if web_manifest %}
<link async="async" rel="manifest" href="{{ web_manifest }}" /> <link async="async" rel="manifest" href="{{ web_manifest }}" />
{% endif %} {% block head_scripts %}{% endblock %} {% endif %} {% block head_scripts %}{% endblock %}
<script type="text/javascript">
const ROOT_PATH = '{{ ROOT_PATH }}'
const RENDERED_ROUTE = '{{ normalize_path(request.path) }}'.replace(
ROOT_PATH.replace(/\/+$/, '') || '/',
''
)
</script>
</head> </head>
<body data-theme="bitcoin"> <body data-theme="bitcoin">
@@ -60,7 +52,9 @@
v-if="g.user && !g.isPublicPage" v-if="g.user && !g.isPublicPage"
></lnbits-header-wallets> ></lnbits-header-wallets>
<!-- block page content from static extensions --> <!-- block page content from static extensions -->
<div v-if="$route.path.startsWith(RENDERED_ROUTE)"> <div
v-if="$route.path.startsWith('{{ normalize_path(request.path) }}')"
>
{% block page %}{% endblock %} {% block page %}{% endblock %}
</div> </div>
<!-- vue router-view --> <!-- vue router-view -->
+5 -35
View File
@@ -774,13 +774,7 @@ include('components/lnbits-error.vue') %}
v-model="password" v-model="password"
name="password" name="password"
:label="$t('password') + ' *'" :label="$t('password') + ' *'"
:type="showPwd ? 'text' : 'password'" type="password"
><template v-slot:append>
<q-icon
:name="showPwd ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPwd = !showPwd"
/> </template
></q-input> ></q-input>
<div class="row justify-end"> <div class="row justify-end">
<q-btn <q-btn
@@ -809,28 +803,16 @@ include('components/lnbits-error.vue') %}
filled filled
v-model="password" v-model="password"
:label="$t('password') + ' *'" :label="$t('password') + ' *'"
:type="showPwd ? 'text' : 'password'" type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]" :rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
><template v-slot:append>
<q-icon
:name="showPwd ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPwd = !showPwd"
/> </template
></q-input> ></q-input>
<q-input <q-input
dense dense
filled filled
v-model="passwordRepeat" v-model="passwordRepeat"
:label="$t('password_repeat') + ' *'" :label="$t('password_repeat') + ' *'"
:type="showPwdRepeat ? 'text' : 'password'" type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]" :rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
><template v-slot:append>
<q-icon
:name="showPwdRepeat ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPwdRepeat = !showPwdRepeat"
/> </template
></q-input> ></q-input>
<div <div
v-if="confirmationMethodsCount > 1" v-if="confirmationMethodsCount > 1"
@@ -943,28 +925,16 @@ include('components/lnbits-error.vue') %}
filled filled
v-model="password" v-model="password"
:label="$t('password') + ' *'" :label="$t('password') + ' *'"
:type="showPwd ? 'text' : 'password'" type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]" :rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
><template v-slot:append>
<q-icon
:name="showPwd ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPwd = !showPwd"
/> </template
></q-input> ></q-input>
<q-input <q-input
dense dense
filled filled
v-model="passwordRepeat" v-model="passwordRepeat"
:label="$t('password_repeat') + ' *'" :label="$t('password_repeat') + ' *'"
:type="showPwdRepeat ? 'text' : 'password'" type="password"
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]" :rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
><template v-slot:append>
<q-icon
:name="showPwdRepeat ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPwdRepeat = !showPwdRepeat"
/> </template
></q-input> ></q-input>
<div class="row justify-end"> <div class="row justify-end">
<q-btn <q-btn
@@ -1,5 +1,5 @@
<template id="lnbits-admin-fiat-providers"> <template id="lnbits-admin-fiat-providers">
<h6 class="q-my-none q-mb-sm"> <h6 class="q-my-none q-mb-sm row items-center q-gutter-sm">
<span v-text="$t('fiat_providers')"></span> <span v-text="$t('fiat_providers')"></span>
<q-btn <q-btn
round round
@@ -7,6 +7,18 @@
@click="hideInputToggle = !hideInputToggle" @click="hideInputToggle = !hideInputToggle"
:icon="hideInputToggle ? 'visibility_off' : 'visibility'" :icon="hideInputToggle ? 'visibility_off' : 'visibility'"
></q-btn> ></q-btn>
<q-toggle
dense
size="sm"
color="warning"
v-model="fiatProvidersAllUsers"
:label="fiatProviderAccessLabel"
>
<q-tooltip>
If enabled for all users, your users may pass a memo that suspends your
account with your fiat providers
</q-tooltip>
</q-toggle>
</h6> </h6>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@@ -46,7 +58,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.stripe_api_secret_key" v-model="formData.stripe_api_secret_key"
:label="$t('secret_key')" :label="$t('secret_key')"
></q-input> ></q-input>
@@ -108,7 +124,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.stripe_webhook_signing_secret" v-model="formData.stripe_webhook_signing_secret"
:label="$t('signing_secret')" :label="$t('signing_secret')"
:hint="$t('signing_secret_hint')" :hint="$t('signing_secret_hint')"
@@ -325,14 +345,22 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.paypal_client_id" v-model="formData.paypal_client_id"
:label="$t('client_id')" :label="$t('client_id')"
></q-input> ></q-input>
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.paypal_client_secret" v-model="formData.paypal_client_secret"
:label="$t('secret_key')" :label="$t('secret_key')"
></q-input> ></q-input>
@@ -394,7 +422,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.paypal_webhook_id" v-model="formData.paypal_webhook_id"
:label="$t('webhook_id')" :label="$t('webhook_id')"
:hint="$t('webhook_id_hint')" :hint="$t('webhook_id_hint')"
@@ -611,7 +643,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.square_access_token" v-model="formData.square_access_token"
:label="$t('access_token')" :label="$t('access_token')"
></q-input> ></q-input>
@@ -688,7 +724,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.square_webhook_signature_key" v-model="formData.square_webhook_signature_key"
:label="$t('signing_secret')" :label="$t('signing_secret')"
:hint="$t('square_webhook_signature_key_hint')" :hint="$t('square_webhook_signature_key_hint')"
@@ -900,7 +940,11 @@
<q-input <q-input
filled filled
class="q-mt-md" class="q-mt-md"
:type="hideInputToggle ? 'password' : 'text'" type="text"
:input-style="secretInputStyle"
autocomplete="off"
autocapitalize="off"
spellcheck="false"
v-model="formData.revolut_api_secret_key" v-model="formData.revolut_api_secret_key"
label="API secret key" label="API secret key"
></q-input> ></q-input>
@@ -320,15 +320,6 @@
> >
</q-toggle> </q-toggle>
</div> </div>
<div class="col-12 col-sm-6 col-lg-2">
<q-toggle
type="bool"
v-model="formData.lnbits_default_burger_menu_background"
color="primary"
:label="$t('burger_menu_background')"
>
</q-toggle>
</div>
</div> </div>
</div> </div>
</q-card-section> </q-card-section>
@@ -26,7 +26,7 @@
@click="g.visibleDrawer = !g.visibleDrawer" @click="g.visibleDrawer = !g.visibleDrawer"
></q-btn> ></q-btn>
<q-toolbar-title> <q-toolbar-title>
<q-btn flat no-caps dense class="q-mr-sm" size="lg" type="a" :href="utils.urlFor('/', true)"> <q-btn flat no-caps dense class="q-mr-sm" size="lg" type="a" href="/">
<q-avatar v-if="g.settings.customLogo" height="30px"> <q-avatar v-if="g.settings.customLogo" height="30px">
<img alt="Logo" :src="g.settings.customLogo" /> <img alt="Logo" :src="g.settings.customLogo" />
</q-avatar> </q-avatar>
@@ -7,9 +7,7 @@
<a :href="logo.url" target="_blank" rel="noopener noreferrer"> <a :href="logo.url" target="_blank" rel="noopener noreferrer">
<q-img <q-img
contain contain
:src=" :src="$q.dark.isActive ? logo.darkSrc : logo.lightSrc"
utils.urlFor($q.dark.isActive ? logo.darkSrc : logo.lightSrc)
"
></q-img> ></q-img>
</a> </a>
</div> </div>
@@ -20,9 +18,7 @@
<a :href="logo.url" target="_blank" rel="noopener noreferrer"> <a :href="logo.url" target="_blank" rel="noopener noreferrer">
<q-img <q-img
contain contain
:src=" :src="$q.dark.isActive ? logo.darkSrc : logo.lightSrc"
utils.urlFor($q.dark.isActive ? logo.darkSrc : logo.lightSrc)
"
></q-img> ></q-img>
</a> </a>
</div> </div>
@@ -20,14 +20,11 @@
clickable clickable
:active="$route.path.startsWith('/' + extension.code)" :active="$route.path.startsWith('/' + extension.code)"
tag="a" tag="a"
:to="`/${extension.code}/`" :to="'/' + extension.code + '/'"
> >
<q-item-section side> <q-item-section side>
<q-avatar size="md"> <q-avatar size="md">
<q-img <q-img :src="extension.tile" style="max-width: 20px"></q-img>
:src="utils.urlFor(extension.tile)"
style="max-width: 20px"
></q-img>
</q-avatar> </q-avatar>
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
+2 -30
View File
@@ -285,9 +285,7 @@
> >
<q-avatar size="32px" class="q-mr-md"> <q-avatar size="32px" class="q-mr-md">
<q-img <q-img
:src=" :src="'{{ static_url_for('static', 'images/google-logo.png') }}'"
utils.urlFor('/static/images/google-logo.png')
"
></q-img> ></q-img>
</q-avatar> </q-avatar>
<div>Google</div> <div>Google</div>
@@ -308,9 +306,7 @@
> >
<q-avatar size="32px" class="q-mr-md"> <q-avatar size="32px" class="q-mr-md">
<q-img <q-img
:src=" :src="'{{ static_url_for('static', 'images/github-logo.png') }}'"
utils.urlFor('/static/images/github-logo.png')
"
></q-img> ></q-img>
</q-avatar> </q-avatar>
<div>GitHub</div> <div>GitHub</div>
@@ -605,30 +601,6 @@
</div> </div>
</div> </div>
<div class="row q-mb-md">
<div class="col-4">
<span v-text="$t('burger_menu_background')"></span>
</div>
<div class="col-8">
<q-toggle
dense
flat
round
icon="menu_open"
v-model="g.burgerMenuChoice"
@update:model-value="
siteCustomisationChanged({burgerMenuChoice: $event})
"
>
<q-tooltip
><span
v-text="$t('toggle_burger_menu_background')"
></span
></q-tooltip>
</q-toggle>
</div>
</div>
<div class="row q-mb-md"> <div class="row q-mb-md">
<div class="col-4"> <div class="col-4">
<span v-text="$t('toggle_darkmode')"></span> <span v-text="$t('toggle_darkmode')"></span>
+1 -1
View File
@@ -136,7 +136,7 @@
</q-card> </q-card>
</div> </div>
<div <div
v-show="chartData.showPaymentTags" v-show="chartData.showPaymentStatus"
class="col-lg-3 col-md-6 col-sm-12 text-center" class="col-lg-3 col-md-6 col-sm-12 text-center"
> >
<q-card class="q-pt-sm"> <q-card class="q-pt-sm">
+2 -6
View File
@@ -216,12 +216,8 @@
<q-card-section class="text-subtitle1"> <q-card-section class="text-subtitle1">
<span v-text="g.settings.adSpaceTitle"></span> <span v-text="g.settings.adSpaceTitle"></span>
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.settings.adSpace"> <a :href="ad[0]" class="lnbits-ad" v-for="ad in g.settings.adSpace">
<q-img <q-img class="q-mb-xs" v-if="$q.dark.isActive" :src="ad[1]"></q-img>
class="q-mb-xs" <q-img class="q-mb-xs" v-else :src="ad[2]"></q-img>
v-if="$q.dark.isActive"
:src="utils.urlFor(ad[1])"
></q-img>
<q-img class="q-mb-xs" v-else :src="utils.urlFor(ad[2])"></q-img>
</a> </a>
</q-card-section> </q-card-section>
</q-card> </q-card>
+3 -3
View File
@@ -94,15 +94,15 @@ class PaymentStatus(NamedTuple):
class PaymentSuccessStatus(PaymentStatus): class PaymentSuccessStatus(PaymentStatus):
paid = True # type: ignore[reportIncompatibleVariableOverride] paid = True
class PaymentFailedStatus(PaymentStatus): class PaymentFailedStatus(PaymentStatus):
paid = False # type: ignore[reportIncompatibleVariableOverride] paid = False
class PaymentPendingStatus(PaymentStatus): class PaymentPendingStatus(PaymentStatus):
paid = None # type: ignore[reportIncompatibleVariableOverride] paid = None
class Wallet(ABC): class Wallet(ABC):
+7 -5
View File
@@ -8,7 +8,7 @@ from loguru import logger
from pydantic import BaseModel from pydantic import BaseModel
from websockets import Subprotocol, connect from websockets import Subprotocol, connect
from lnbits import bolt11 as bolt11_lib from lnbits import bolt11
from lnbits.helpers import normalize_endpoint from lnbits.helpers import normalize_endpoint
from lnbits.settings import settings from lnbits.settings import settings
@@ -164,13 +164,15 @@ class BlinkWallet(Wallet):
ok=False, error_message=f"Unable to connect to {self.endpoint}." ok=False, error_message=f"Unable to connect to {self.endpoint}."
) )
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: async def pay_invoice(
self, bolt11_invoice: str, fee_limit_msat: int
) -> PaymentResponse:
# https://dev.blink.sv/api/btc-ln-send # https://dev.blink.sv/api/btc-ln-send
# Future: add check fee estimate is < fee_limit_msat before paying invoice # Future: add check fee estimate is < fee_limit_msat before paying invoice
payment_variables = { payment_variables = {
"input": { "input": {
"paymentRequest": bolt11, "paymentRequest": bolt11_invoice,
"walletId": self.wallet_id, "walletId": self.wallet_id,
"memo": "Payment memo", "memo": "Payment memo",
} }
@@ -188,7 +190,7 @@ class BlinkWallet(Wallet):
error_message = errors[0].get("message") error_message = errors[0].get("message")
return PaymentResponse(ok=False, error_message=error_message) return PaymentResponse(ok=False, error_message=error_message)
checking_id = bolt11_lib.decode(bolt11).payment_hash checking_id = bolt11.decode(bolt11_invoice).payment_hash
payment_status = await self.get_payment_status(checking_id) payment_status = await self.get_payment_status(checking_id)
fee_msat = payment_status.fee_msat fee_msat = payment_status.fee_msat
@@ -197,7 +199,7 @@ class BlinkWallet(Wallet):
ok=True, checking_id=checking_id, fee_msat=fee_msat, preimage=preimage ok=True, checking_id=checking_id, fee_msat=fee_msat, preimage=preimage
) )
except Exception as exc: except Exception as exc:
logger.info(f"Failed to pay invoice {bolt11}") logger.info(f"Failed to pay invoice {bolt11_invoice}")
logger.warning(exc) logger.warning(exc)
return PaymentResponse( return PaymentResponse(
error_message=f"Unable to connect to {self.endpoint}." error_message=f"Unable to connect to {self.endpoint}."
+2 -4
View File
@@ -19,7 +19,7 @@ else:
from bolt11 import Bolt11Exception from bolt11 import Bolt11Exception
from bolt11 import decode as bolt11_decode from bolt11 import decode as bolt11_decode
from breez_sdk import ( # type: ignore[reportMissingImports] from breez_sdk import (
BreezEvent, BreezEvent,
ConnectRequest, ConnectRequest,
EnvironmentType, EnvironmentType,
@@ -39,9 +39,7 @@ else:
default_config, default_config,
mnemonic_to_seed, mnemonic_to_seed,
) )
from breez_sdk import ( from breez_sdk import PaymentStatus as BreezPaymentStatus
PaymentStatus as BreezPaymentStatus, # type: ignore[reportMissingImports]
)
from loguru import logger from loguru import logger
from lnbits.settings import settings from lnbits.settings import settings
+1 -1
View File
@@ -18,7 +18,7 @@ else:
from pathlib import Path from pathlib import Path
from bolt11 import decode as bolt11_decode from bolt11 import decode as bolt11_decode
from breez_sdk_liquid import ( # type: ignore[reportMissingImports] from breez_sdk_liquid import (
ConnectRequest, ConnectRequest,
EventListener, EventListener,
GetInfoResponse, GetInfoResponse,
+2 -2
View File
@@ -103,7 +103,7 @@ class FakeWallet(Wallet):
preimage=preimage.hex(), preimage=preimage.hex(),
) )
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: async def pay_invoice(self, bolt11: str, _: int) -> PaymentResponse:
try: try:
invoice = decode(bolt11) invoice = decode(bolt11)
except Bolt11Exception as exc: except Bolt11Exception as exc:
@@ -130,7 +130,7 @@ class FakeWallet(Wallet):
return PaymentPendingStatus() return PaymentPendingStatus()
return PaymentFailedStatus() return PaymentFailedStatus()
async def get_payment_status(self, checking_id: str) -> PaymentStatus: async def get_payment_status(self, _: str) -> PaymentStatus:
return PaymentPendingStatus() return PaymentPendingStatus()
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
+1 -3
View File
@@ -118,7 +118,7 @@ class LndWallet(Wallet):
cert = open(cert_path, "rb").read() cert = open(cert_path, "rb").read()
creds = grpc.ssl_channel_credentials(cert) creds = grpc.ssl_channel_credentials(cert)
auth_creds = grpc.metadata_call_credentials(self.metadata_callback) # type: ignore[reportArgumentType] auth_creds = grpc.metadata_call_credentials(self.metadata_callback)
composite_creds = grpc.composite_channel_credentials(creds, auth_creds) composite_creds = grpc.composite_channel_credentials(creds, auth_creds)
channel = grpc.aio.secure_channel( channel = grpc.aio.secure_channel(
f"{self.endpoint}:{self.port}", composite_creds f"{self.endpoint}:{self.port}", composite_creds
@@ -192,8 +192,6 @@ class LndWallet(Wallet):
fee_limit_msat=fee_limit_msat, fee_limit_msat=fee_limit_msat,
timeout_seconds=30, timeout_seconds=30,
no_inflight_updates=True, no_inflight_updates=True,
max_parts=16,
time_pref=0.9,
) )
try: try:
res: Payment = await self.router_rpc.SendPaymentV2(req).read() res: Payment = await self.router_rpc.SendPaymentV2(req).read()
+2 -8
View File
@@ -88,8 +88,6 @@ class NWCWallet(Wallet):
payment_data = await self.conn.call( payment_data = await self.conn.call(
"lookup_invoice", {"payment_hash": payment["checking_id"]} "lookup_invoice", {"payment_hash": payment["checking_id"]}
) )
if payment_data.get("payment_hash") != payment["checking_id"]:
raise Exception("Mismatched payment hash")
settled = ( settled = (
"settled_at" in payment_data "settled_at" in payment_data
and payment_data["settled_at"] and payment_data["settled_at"]
@@ -266,8 +264,6 @@ class NWCWallet(Wallet):
payment_data = await self.conn.call( payment_data = await self.conn.call(
"lookup_invoice", {"payment_hash": checking_id} "lookup_invoice", {"payment_hash": checking_id}
) )
if payment_data.get("payment_hash") != checking_id:
raise Exception("Mismatched payment hash")
settled = payment_data.get("settled_at", None) and payment_data.get( settled = payment_data.get("settled_at", None) and payment_data.get(
"preimage", None "preimage", None
) )
@@ -524,9 +520,8 @@ class NWCConnection:
""" """
sub_id = cast(str, msg[1]) sub_id = cast(str, msg[1])
event = cast(dict, msg[2]) event = cast(dict, msg[2])
# Ensure the event is valid (do not trust relays) if not verify_event(event): # Ensure the event is valid (do not trust relays)
if not verify_event(event) or event.get("pubkey") != self.service_pubkey_hex: raise Exception("Invalid event signature")
raise Exception("Invalid event")
tags = event["tags"] tags = event["tags"]
if event["kind"] == 13194: # An info event if event["kind"] == 13194: # An info event
# info events are handled specially, # info events are handled specially,
@@ -692,7 +687,6 @@ class NWCConnection:
"#p": [self.account_public_key_hex], "#p": [self.account_public_key_hex],
"#e": [event["id"]], "#e": [event["id"]],
"since": event["created_at"], "since": event["created_at"],
"authors": [self.service_pubkey_hex],
} }
sub_id = self._get_new_subid() sub_id = self._get_new_subid()
# register a future to receive the response asynchronously # register a future to receive the response asynchronously
+5 -8
View File
@@ -24,7 +24,7 @@
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"concat": "^1.0.3", "concat": "^1.0.3",
"prettier": "^3.8.3", "prettier": "^3.8.3",
"pyright": "1.1.409", "pyright": "1.1.289",
"sass": "^1.99.0", "sass": "^1.99.0",
"terser": "^5.47.1" "terser": "^5.47.1"
} }
@@ -1808,9 +1808,9 @@
} }
}, },
"node_modules/pyright": { "node_modules/pyright": {
"version": "1.1.409", "version": "1.1.289",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.409.tgz", "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.289.tgz",
"integrity": "sha512-13VFQyw4mJzshZxcxiYbNjo1hG/WHSRDj70Y3lbJEHqCkI2dvBAUTti8VV6Ezsr5gT93pFvC0e/jAQS4JdHarA==", "integrity": "sha512-fG3STxnwAt3i7bxbXUPJdYNFrcOWHLwCSEOySH2foUqtYdzWLcxDez0Kgl1X8LMQx0arMJ6HRkKghxfRD1/z6g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@@ -1818,10 +1818,7 @@
"pyright-langserver": "langserver.index.js" "pyright-langserver": "langserver.index.js"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=12.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
} }
}, },
"node_modules/qrcode.vue": { "node_modules/qrcode.vue": {
+1 -1
View File
@@ -16,7 +16,7 @@
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"concat": "^1.0.3", "concat": "^1.0.3",
"prettier": "^3.8.3", "prettier": "^3.8.3",
"pyright": "1.1.409", "pyright": "1.1.289",
"sass": "^1.99.0", "sass": "^1.99.0",
"terser": "^5.47.1" "terser": "^5.47.1"
}, },
Generated
+6 -19
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. # This file is automatically @generated by Poetry 2.4.0 and should not be changed by hand.
[[package]] [[package]]
name = "aiohappyeyeballs" name = "aiohappyeyeballs"
@@ -665,19 +665,6 @@ bitstring = "*"
click = "*" click = "*"
coincurve = "*" coincurve = "*"
[[package]]
name = "boltz-client"
version = "0.4.0"
description = "Boltz Swap library"
optional = true
python-versions = ">=3.10"
groups = ["main"]
markers = "extra == \"liquid\""
files = [
{file = "boltz_client-0.4.0-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:ed0b520209cf1b05a8523002f5d8f26fa29c04d2faecc9cbde3621b4ddc417e6"},
{file = "boltz_client-0.4.0.tar.gz", hash = "sha256:a3f5a6b637350267856e3ab680cd92158de720fa2d5805fc075e4583d020cb2a"},
]
[[package]] [[package]]
name = "breez-sdk" name = "breez-sdk"
version = "0.8.0" version = "0.8.0"
@@ -2147,7 +2134,7 @@ files = [
[package.dependencies] [package.dependencies]
attrs = ">=22.2.0" attrs = ">=22.2.0"
jsonschema-specifications = ">=2023.03.6" jsonschema-specifications = ">=2023.3.6"
referencing = ">=0.28.4" referencing = ">=0.28.4"
rpds-py = ">=0.25.0" rpds-py = ">=0.25.0"
@@ -2308,7 +2295,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras] [package.extras]
dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==0.910) ; python_version < \"3.6\"", "mypy (==0.971) ; python_version == \"3.6\"", "mypy (==1.13.0) ; python_version >= \"3.8\"", "mypy (==1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""]
[[package]] [[package]]
name = "markdown-it-py" name = "markdown-it-py"
@@ -3407,7 +3394,7 @@ version = "5.1.2"
description = "Call stack profiler for Python. Shows you why your code is slow!" description = "Call stack profiler for Python. Shows you why your code is slow!"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["dev"]
files = [ files = [
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f224fe80ba288a00980af298d3808219f9d246fd95b4f91729c9c33a0dc54fe6"}, {file = "pyinstrument-5.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f224fe80ba288a00980af298d3808219f9d246fd95b4f91729c9c33a0dc54fe6"},
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7df09fc0d5b72daf48b73cdf07738761bff7f656c81aff686b3ccdd7d2abe236"}, {file = "pyinstrument-5.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7df09fc0d5b72daf48b73cdf07738761bff7f656c81aff686b3ccdd7d2abe236"},
@@ -5117,10 +5104,10 @@ propcache = ">=0.2.1"
[extras] [extras]
breez = ["breez-sdk", "breez-sdk-liquid"] breez = ["breez-sdk", "breez-sdk-liquid"]
liquid = ["boltz-client", "wallycore"] liquid = ["wallycore"]
migration = ["psycopg2-binary"] migration = ["psycopg2-binary"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.10,<3.13" python-versions = ">=3.10,<3.13"
content-hash = "7c70bdad0089089d383cf8f54c37c4473180cea175689b91322beaed1ab42e94" content-hash = "4dd6ccfc459064fb5335531d4ceb9c0f228279b1c95d0911e895232a51b90006"
+3 -3
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "lnbits" name = "lnbits"
version = "1.5.5-rc2" version = "1.5.4"
requires-python = ">=3.10,<3.13" requires-python = ">=3.10,<3.13"
description = "LNbits, free and open-source Lightning wallet and accounts system." description = "LNbits, free and open-source Lightning wallet and accounts system."
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }] authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
@@ -52,7 +52,6 @@ dependencies = [
"python-dotenv~=1.2.1", "python-dotenv~=1.2.1",
"greenlet~=3.3.0", "greenlet~=3.3.0",
"urllib3>=2.7.0", "urllib3>=2.7.0",
"pyinstrument>=5.1.2",
] ]
[project.scripts] [project.scripts]
@@ -61,7 +60,7 @@ lnbits-cli = "lnbits.commands:main"
[project.optional-dependencies] [project.optional-dependencies]
breez = ["breez-sdk~=0.8.0", "breez-sdk-liquid~=0.11.11"] breez = ["breez-sdk~=0.8.0", "breez-sdk-liquid~=0.11.11"]
liquid = ["wallycore~=1.5.1", "boltz-client==0.4.0"] liquid = ["wallycore~=1.5.1"]
migration = ["psycopg2-binary~=2.9.11"] migration = ["psycopg2-binary~=2.9.11"]
[dependency-groups] [dependency-groups]
@@ -85,6 +84,7 @@ dev = [
"types-mock~=5.2.0.20250924", "types-mock~=5.2.0.20250924",
"mock~=5.2.0", "mock~=5.2.0",
"grpcio-tools~=1.76.0", "grpcio-tools~=1.76.0",
"pyinstrument>=5.1.2",
] ]
[tool.uv] [tool.uv]
+1 -35
View File
@@ -72,42 +72,8 @@ async def test_auth_api_sso_login_and_callback(http_client: AsyncClient, mocker)
login_sso = _FakeSSO() login_sso = _FakeSSO()
mocker.patch("lnbits.core.views.auth_api._new_sso", return_value=login_sso) mocker.patch("lnbits.core.views.auth_api._new_sso", return_value=login_sso)
unauthenticated = await http_client.get(
f"/api/v1/auth/{provider}", params={"user_id": user.id}
)
assert unauthenticated.status_code == 403
assert unauthenticated.json()["detail"] == "User ID mismatch."
other_user = await create_user_account(
Account(
id=uuid4().hex,
username=f"user_{uuid4().hex[:8]}",
email=f"user_{uuid4().hex[:8]}@lnbits.com",
)
)
other_login = await http_client.post(
"/api/v1/auth/usr", json={"usr": other_user.id}
)
http_client.cookies.clear()
assert other_login.status_code == 200
other_headers = {
"Authorization": f"Bearer {other_login.json()['access_token']}",
}
wrong_user = await http_client.get(
f"/api/v1/auth/{provider}",
params={"user_id": user.id},
headers=other_headers,
)
assert wrong_user.status_code == 403
assert wrong_user.json()["detail"] == "User ID mismatch."
login = await http_client.post("/api/v1/auth/usr", json={"usr": user.id})
http_client.cookies.clear()
assert login.status_code == 200
headers = {"Authorization": f"Bearer {login.json()['access_token']}"}
response = await http_client.get( response = await http_client.get(
f"/api/v1/auth/{provider}", params={"user_id": user.id}, headers=headers f"/api/v1/auth/{provider}", params={"user_id": user.id}
) )
assert response.status_code == 307 assert response.status_code == 307
assert response.headers["location"] == "https://example.com/sso/login" assert response.headers["location"] == "https://example.com/sso/login"
+20 -6
View File
@@ -194,7 +194,7 @@ async def test_callback_api_handles_revolut_subscription_event(
settings.revolut_api_secret_key = "revolut-secret" settings.revolut_api_secret_key = "revolut-secret"
settings.revolut_api_version = "2026-04-20" settings.revolut_api_version = "2026-04-20"
revolut_provider = RevolutWallet() revolut_provider = RevolutWallet()
get_subscription_mock = mocker.patch.object( mocker.patch.object(
revolut_provider, revolut_provider,
"get_subscription", "get_subscription",
return_value={ return_value={
@@ -253,10 +253,23 @@ async def test_callback_api_handles_revolut_subscription_event(
} }
) )
get_subscription_mock.assert_not_awaited() assert create_wallet_invoice_mock.await_count == 1
create_wallet_invoice_mock.assert_not_awaited() called_wallet_id, invoice = create_wallet_invoice_mock.await_args.args
update_payment_mock.assert_not_awaited() assert called_wallet_id == "wallet_1"
fiat_status_mock.assert_not_awaited() assert invoice.amount == 9.25
assert invoice.memo == "Revolut Members"
assert invoice.external_id == "SUBSCRIPTION_1"
assert invoice.internal is True
assert invoice.extra["fiat_method"] == "subscription"
assert invoice.extra["subscription"]["checking_id"] == "order_ORDER_SUB_1"
assert payment.fiat_provider == "revolut"
assert payment.fee == -2
assert payment.extra["fiat_checking_id"] == "order_ORDER_SUB_1"
assert payment.checking_id == "fiat_revolut_order_ORDER_SUB_1"
update_payment_mock.assert_awaited_once_with(
payment, "fiat_revolut_order_ORDER_SUB_1"
)
fiat_status_mock.assert_awaited_once_with(payment)
@pytest.mark.anyio @pytest.mark.anyio
@@ -340,9 +353,10 @@ async def test_callback_api_handles_revolut_subscription_order_event(
assert get_payment_mock.await_count == 2 assert get_payment_mock.await_count == 2
get_payment_mock.assert_any_await("fiat_revolut_order_ORDER_SUB_1") get_payment_mock.assert_any_await("fiat_revolut_order_ORDER_SUB_1")
assert get_order_mock.await_count == 1 assert get_order_mock.await_count == 2
assert [call.args for call in get_subscription_mock.await_args_list] == [ assert [call.args for call in get_subscription_mock.await_args_list] == [
("SUBSCRIPTION_1",), ("SUBSCRIPTION_1",),
("SUBSCRIPTION_1",),
] ]
assert create_wallet_invoice_mock.await_count == 1 assert create_wallet_invoice_mock.await_count == 1
called_wallet_id, invoice = create_wallet_invoice_mock.await_args.args called_wallet_id, invoice = create_wallet_invoice_mock.await_args.args
+1 -2
View File
@@ -1,4 +1,3 @@
from typing import cast
from uuid import uuid4 from uuid import uuid4
import pytest import pytest
@@ -107,7 +106,7 @@ async def test_lnurl_api_auth_and_pay_flow(mocker):
await api_perform_lnurlauth(auth_response, wallet_info) await api_perform_lnurlauth(auth_response, wallet_info)
action_response = LnurlPayActionResponse( action_response = LnurlPayActionResponse(
pr=cast(LightningInvoice, LightningInvoice(TEST_BOLT11)), pr=LightningInvoice(TEST_BOLT11),
disposable=False, disposable=False,
successAction=parse_obj_as(MessageAction, {"message": "paid"}), successAction=parse_obj_as(MessageAction, {"message": "paid"}),
) )
+2 -160
View File
@@ -6,7 +6,7 @@ import pytest
from fastapi import HTTPException from fastapi import HTTPException
from pydantic import ValidationError from pydantic import ValidationError
from lnbits.core.crud.payments import create_payment, get_payment, get_payments from lnbits.core.crud.payments import create_payment, get_payments
from lnbits.core.models import Account, CreateInvoice, PaymentFilters, PaymentState from lnbits.core.models import Account, CreateInvoice, PaymentFilters, PaymentState
from lnbits.core.models.payments import CancelInvoice, CreatePayment, SettleInvoice from lnbits.core.models.payments import CancelInvoice, CreatePayment, SettleInvoice
from lnbits.core.models.users import AccountId from lnbits.core.models.users import AccountId
@@ -161,7 +161,7 @@ async def test_payment_api_fee_reserve_and_hold_invoice_actions(mocker):
wallet.id, CreateInvoice(out=False, amount=42, memo="reserve") wallet.id, CreateInvoice(out=False, amount=42, memo="reserve")
) )
reserve = await api_payments_fee_reserve(invoice.bolt11) reserve = await api_payments_fee_reserve(invoice.bolt11)
assert json.loads(bytes(reserve.body))["fee_reserve"] >= 0 assert json.loads(reserve.body)["fee_reserve"] >= 0
with pytest.raises(HTTPException, match="Invoice has no amount."): with pytest.raises(HTTPException, match="Invoice has no amount."):
await api_payments_fee_reserve(ZERO_AMOUNT_INVOICE) await api_payments_fee_reserve(ZERO_AMOUNT_INVOICE)
@@ -218,164 +218,6 @@ async def test_payment_api_fee_reserve_and_hold_invoice_actions(mocker):
cancel_mock.assert_awaited_once() cancel_mock.assert_awaited_once()
@pytest.mark.anyio
async def test_payment_extra_update_appends_new_keys(
client,
to_wallet,
adminkey_headers_to,
):
payment_hash = uuid4().hex
checking_id = await _create_payment(
to_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
tag="splitpayments",
)
response = await client.patch(
"/api/v1/payments/extra",
headers=adminkey_headers_to,
json={
"payment_hash": payment_hash,
"extra": {"child": "daughter", "compliance_note": "reviewed"},
},
)
assert response.status_code == 200
extra = response.json()["extra"]
assert extra["tag"] == "splitpayments"
assert extra["child"] == "daughter"
assert extra["compliance_note"] == "reviewed"
payment = await get_payment(checking_id)
assert payment.extra == extra
@pytest.mark.anyio
async def test_payment_extra_update_creates_extra_when_missing(
client,
to_wallet,
adminkey_headers_to,
):
payment_hash = uuid4().hex
checking_id = await _create_payment(
to_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
)
response = await client.patch(
"/api/v1/payments/extra",
headers=adminkey_headers_to,
json={"payment_hash": payment_hash, "extra": {"note": "reviewed"}},
)
assert response.status_code == 200
assert response.json()["extra"] == {"note": "reviewed"}
payment = await get_payment(checking_id)
assert payment.extra == {"note": "reviewed"}
@pytest.mark.anyio
async def test_payment_extra_update_rejects_existing_keys(
client,
to_wallet,
adminkey_headers_to,
):
payment_hash = uuid4().hex
checking_id = await _create_payment(
to_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
tag="original",
)
response = await client.patch(
"/api/v1/payments/extra",
headers=adminkey_headers_to,
json={"payment_hash": payment_hash, "extra": {"tag": "overwritten"}},
)
assert response.status_code == 400
assert response.json()["detail"] == "Extra keys already exist: tag."
payment = await get_payment(checking_id)
assert payment.extra == {"tag": "original"}
@pytest.mark.anyio
async def test_payment_extra_update_requires_admin_key(
client,
to_wallet,
inkey_headers_to,
):
payment_hash = uuid4().hex
await _create_payment(
to_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
)
response = await client.patch(
"/api/v1/payments/extra",
headers=inkey_headers_to,
json={"payment_hash": payment_hash, "extra": {"note": "invoice key"}},
)
assert response.status_code == 403
assert response.json()["detail"] == "Invalid adminkey."
@pytest.mark.anyio
async def test_payment_extra_update_is_wallet_scoped(
client,
from_wallet,
adminkey_headers_to,
):
payment_hash = uuid4().hex
await _create_payment(
from_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
)
response = await client.patch(
"/api/v1/payments/extra",
headers=adminkey_headers_to,
json={"payment_hash": payment_hash, "extra": {"note": "wrong wallet"}},
)
assert response.status_code == 404
assert response.json()["detail"] == "Payment does not exist."
@pytest.mark.anyio
async def test_payment_extra_update_requires_successful_payment(
client,
to_wallet,
adminkey_headers_to,
):
payment_hash = uuid4().hex
await _create_payment(
to_wallet.id,
amount_msat=1_000,
payment_hash=payment_hash,
status=PaymentState.PENDING,
)
response = await client.patch(
"/api/v1/payments/extra",
headers=adminkey_headers_to,
json={"payment_hash": payment_hash, "extra": {"note": "too early"}},
)
assert response.status_code == 400
assert (
response.json()["detail"] == "Payment extra can only be updated after success."
)
async def _create_payment( async def _create_payment(
wallet_id: str, wallet_id: str,
*, *,
+36
View File
@@ -76,6 +76,7 @@ class MockHTTPClient:
def fiat_provider_test_settings(settings: Settings): def fiat_provider_test_settings(settings: Settings):
original_lnbits_running = settings.lnbits_running original_lnbits_running = settings.lnbits_running
original_allowed_currencies = settings.lnbits_allowed_currencies original_allowed_currencies = settings.lnbits_allowed_currencies
original_fiat_providers_admin_only = settings.fiat_providers_admin_only
original_paypal_enabled = settings.paypal_enabled original_paypal_enabled = settings.paypal_enabled
original_square_enabled = settings.square_enabled original_square_enabled = settings.square_enabled
original_square_api_endpoint = settings.square_api_endpoint original_square_api_endpoint = settings.square_api_endpoint
@@ -95,12 +96,14 @@ def fiat_provider_test_settings(settings: Settings):
original_revolut_webhook_signing_secret = settings.revolut_webhook_signing_secret original_revolut_webhook_signing_secret = settings.revolut_webhook_signing_secret
original_revolut_limits = settings.revolut_limits.copy(deep=True) original_revolut_limits = settings.revolut_limits.copy(deep=True)
settings.lnbits_allowed_currencies = [] settings.lnbits_allowed_currencies = []
settings.fiat_providers_admin_only = False
settings.paypal_enabled = False settings.paypal_enabled = False
settings.square_enabled = False settings.square_enabled = False
settings.revolut_enabled = False settings.revolut_enabled = False
yield yield
settings.lnbits_running = original_lnbits_running settings.lnbits_running = original_lnbits_running
settings.lnbits_allowed_currencies = original_allowed_currencies settings.lnbits_allowed_currencies = original_allowed_currencies
settings.fiat_providers_admin_only = original_fiat_providers_admin_only
settings.paypal_enabled = original_paypal_enabled settings.paypal_enabled = original_paypal_enabled
settings.square_enabled = original_square_enabled settings.square_enabled = original_square_enabled
settings.square_api_endpoint = original_square_api_endpoint settings.square_api_endpoint = original_square_api_endpoint
@@ -217,6 +220,39 @@ async def test_create_wallet_fiat_invoice_allowed_users(
assert user.fiat_providers == ["revolut"] assert user.fiat_providers == ["revolut"]
@pytest.mark.anyio
async def test_fiat_providers_admin_only_default(to_user: User, settings: Settings):
original_admin_users = list(settings.lnbits_admin_users)
try:
settings.fiat_providers_admin_only = True
settings.stripe_enabled = True
user = await get_user(to_user.id)
assert user
assert user.fiat_providers == []
settings.lnbits_admin_users.append(to_user.id)
user = await get_user(to_user.id)
assert user
assert user.fiat_providers == ["stripe"]
finally:
settings.lnbits_admin_users = original_admin_users
@pytest.mark.anyio
async def test_create_wallet_fiat_invoice_admin_only_rejects_non_admin(
to_wallet: Wallet, settings: Settings
):
settings.fiat_providers_admin_only = True
settings.stripe_enabled = True
invoice_data = CreateInvoice(
unit="USD", amount=1.0, memo="Test", fiat_provider="stripe"
)
with pytest.raises(ValueError, match="available to admins only"):
await payments.create_fiat_invoice(to_wallet.id, invoice_data)
@pytest.mark.anyio @pytest.mark.anyio
async def test_create_wallet_fiat_invoice_fiat_limits_fail( async def test_create_wallet_fiat_invoice_fiat_limits_fail(
to_wallet: Wallet, settings: Settings, mocker: MockerFixture to_wallet: Wallet, settings: Settings, mocker: MockerFixture
+3 -6
View File
@@ -1,4 +1,3 @@
from typing import cast
from uuid import uuid4 from uuid import uuid4
import pytest import pytest
@@ -87,9 +86,7 @@ async def test_get_pr_from_lnurl_success_and_error(mocker: MockerFixture):
mocker.patch( mocker.patch(
"lnbits.core.services.lnurl.execute_pay_request", "lnbits.core.services.lnurl.execute_pay_request",
mocker.AsyncMock( mocker.AsyncMock(
return_value=LnurlPayActionResponse( return_value=LnurlPayActionResponse(pr=LightningInvoice(TEST_BOLT11))
pr=cast(LightningInvoice, LightningInvoice(TEST_BOLT11))
)
), ),
) )
@@ -109,7 +106,7 @@ async def test_fetch_lnurl_pay_request_converts_currency_and_stores_paylink(
): ):
pay_response = make_lnurl_pay_response(min_sendable_msat=1, text="Test") pay_response = make_lnurl_pay_response(min_sendable_msat=1, text="Test")
action_response = LnurlPayActionResponse( action_response = LnurlPayActionResponse(
pr=cast(LightningInvoice, LightningInvoice(TEST_BOLT11)), disposable=False pr=LightningInvoice(TEST_BOLT11), disposable=False
) )
mocker.patch( mocker.patch(
"lnbits.core.services.lnurl.fiat_amount_as_satoshis", "lnbits.core.services.lnurl.fiat_amount_as_satoshis",
@@ -146,7 +143,7 @@ async def test_store_paylink_appends_and_updates_existing():
wallet = await _create_wallet() wallet = await _create_wallet()
pay_response = make_lnurl_pay_response(min_sendable_msat=1, text="Test") pay_response = make_lnurl_pay_response(min_sendable_msat=1, text="Test")
action_response = LnurlPayActionResponse( action_response = LnurlPayActionResponse(
pr=cast(LightningInvoice, LightningInvoice(TEST_BOLT11)), disposable=False pr=LightningInvoice(TEST_BOLT11), disposable=False
) )
await store_paylink( await store_paylink(
-2
View File
@@ -37,14 +37,12 @@ def test_dict_to_settings_parses_known_values():
{ {
"lnbits_site_title": "Test Title", "lnbits_site_title": "Test Title",
"lnbits_service_fee": 5, "lnbits_service_fee": 5,
"lnbits_default_burger_menu_background": False,
"ignored_field": "ignored", "ignored_field": "ignored",
} }
) )
assert parsed.lnbits_site_title == "Test Title" assert parsed.lnbits_site_title == "Test Title"
assert parsed.lnbits_service_fee == 5 assert parsed.lnbits_service_fee == 5
assert parsed.lnbits_default_burger_menu_background is False
assert not hasattr(parsed, "ignored_field") assert not hasattr(parsed, "ignored_field")
+1 -2
View File
@@ -1,4 +1,3 @@
from typing import Any
from uuid import uuid4 from uuid import uuid4
import pytest import pytest
@@ -67,7 +66,7 @@ async def test_create_user_account_no_check_rejects_duplicate_identity_fields(
existing = _account(**existing_data) existing = _account(**existing_data)
await create_account(existing) await create_account(existing)
resolved: dict[str, Any] = { resolved = {
key: (value(existing) if callable(value) else value) key: (value(existing) if callable(value) else value)
for key, value in new_data.items() for key, value in new_data.items()
} }
+4 -14
View File
@@ -1,5 +1,3 @@
from typing import Any
import pytest import pytest
from pytest_mock.plugin import MockerFixture from pytest_mock.plugin import MockerFixture
@@ -16,22 +14,22 @@ from lnbits.settings import (
set_cli_settings, set_cli_settings,
) )
lnurlp_redirect_path: dict[str, Any] = { lnurlp_redirect_path = {
"from_path": "/.well-known/lnurlp", "from_path": "/.well-known/lnurlp",
"redirect_to_path": "/api/v1/well-known", "redirect_to_path": "/api/v1/well-known",
} }
lnurlp_redirect_path_with_headers: dict[str, Any] = { lnurlp_redirect_path_with_headers = {
"from_path": "/.well-known/lnurlp", "from_path": "/.well-known/lnurlp",
"redirect_to_path": "/api/v1/well-known", "redirect_to_path": "/api/v1/well-known",
"header_filters": {"accept": "application/nostr+json"}, "header_filters": {"accept": "application/nostr+json"},
} }
lnaddress_redirect_path: dict[str, Any] = { lnaddress_redirect_path = {
"from_path": "/.well-known/lnurlp", "from_path": "/.well-known/lnurlp",
"redirect_to_path": "/api/v1/well-known", "redirect_to_path": "/api/v1/well-known",
} }
nostrrelay_redirect_path: dict[str, Any] = { nostrrelay_redirect_path = {
"from_path": "/", "from_path": "/",
"redirect_to_path": "/api/v1/relay-info", "redirect_to_path": "/api/v1/relay-info",
"header_filters": {"accept": "application/nostr+json"}, "header_filters": {"accept": "application/nostr+json"},
@@ -234,14 +232,6 @@ def test_installed_extensions_settings_activate_and_deactivate_paths():
assert installed.find_extension_redirect("/.well-known/lnurlp", []) is None assert installed.find_extension_redirect("/.well-known/lnurlp", []) is None
def test_public_settings_include_burger_menu_background(settings: Settings):
settings.lnbits_default_burger_menu_background = False
public_settings = PublicSettings.from_settings(settings)
assert public_settings.default_burger_menu_background is False
def test_installed_extensions_settings_detects_conflicting_redirects(): def test_installed_extensions_settings_detects_conflicting_redirects():
installed = InstalledExtensionsSettings( installed = InstalledExtensionsSettings(
lnbits_extensions_redirects=[ lnbits_extensions_redirects=[
+1 -2
View File
@@ -1,5 +1,4 @@
import os import os
from typing import cast
import pytest import pytest
from loguru import logger from loguru import logger
@@ -30,7 +29,7 @@ logger.info(f"settings.blink_api_endpoint: {settings.blink_api_endpoint}")
logger.info(f"settings.blink_token: {settings.blink_token}") logger.info(f"settings.blink_token: {settings.blink_token}")
set_funding_source() set_funding_source()
funding_source = cast(BlinkWallet, get_funding_source()) funding_source = get_funding_source()
assert isinstance(funding_source, BlinkWallet) assert isinstance(funding_source, BlinkWallet)
+4 -7
View File
@@ -1,5 +1,4 @@
import importlib import importlib
from typing import Any
from unittest.mock import AsyncMock, Mock from unittest.mock import AsyncMock, Mock
import pytest import pytest
@@ -81,9 +80,7 @@ def _check_calls(expected_calls):
for func_call in func_calls: for func_call in func_calls:
req = func_call["request_data"] req = func_call["request_data"]
args = req["args"] if "args" in req else {} args = req["args"] if "args" in req else {}
kwargs: dict[str, Any] = ( kwargs = _eval_dict(req["kwargs"]) if "kwargs" in req else {}
_eval_dict(req["kwargs"]) or {} if "kwargs" in req else {}
)
if "klass" in req: if "klass" in req:
*rest, cls = req["klass"].split(".") *rest, cls = req["klass"].split(".")
@@ -169,7 +166,7 @@ def _mock_field(field):
return response return response
def _eval_dict(data: dict | None) -> dict[str, Any] | None: def _eval_dict(data: dict | None) -> dict | None:
fn_prefix = "__eval__:" fn_prefix = "__eval__:"
if not data: if not data:
return data return data
@@ -218,9 +215,9 @@ def _data_mock(data: dict) -> Mock:
def _raise(error: dict | None): def _raise(error: dict | None):
if not error: if not error:
return Exception() return Exception()
data: dict[str, Any] = error["data"] if "data" in error else {} data = error["data"] if "data" in error else None
if "module" not in error or "class" not in error: if "module" not in error or "class" not in error:
return Exception(data or None) return Exception(data)
error_module = importlib.import_module(error["module"]) error_module = importlib.import_module(error["module"])
error_class = getattr(error_module, error["class"]) error_class = getattr(error_module, error["class"])
Generated
+234 -242
View File
@@ -3,16 +3,16 @@ revision = 3
requires-python = ">=3.10, <3.13" requires-python = ">=3.10, <3.13"
[options] [options]
exclude-newer = "2026-05-27T19:33:23.836682466Z" exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
exclude-newer-span = "P1W" exclude-newer-span = "P1W"
[[package]] [[package]]
name = "aiohappyeyeballs" name = "aiohappyeyeballs"
version = "2.6.2" version = "2.6.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
] ]
[[package]] [[package]]
@@ -385,15 +385,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/9e/78e59887cbf94116bdc890af7726ae264d55df14f1c777724c656e8a35fe/bolt11-2.1.1-py3-none-any.whl", hash = "sha256:fd4edb9e73e27bf5e017f47c97f7c6827b523fcf9cab152b123961ca78323e2d", size = 17102, upload-time = "2025-03-12T13:33:08.142Z" }, { url = "https://files.pythonhosted.org/packages/9d/9e/78e59887cbf94116bdc890af7726ae264d55df14f1c777724c656e8a35fe/bolt11-2.1.1-py3-none-any.whl", hash = "sha256:fd4edb9e73e27bf5e017f47c97f7c6827b523fcf9cab152b123961ca78323e2d", size = 17102, upload-time = "2025-03-12T13:33:08.142Z" },
] ]
[[package]]
name = "boltz-client"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/97/c0d3d0cb5a6f70511fc22549d661406fa41859971b3d8e807c9122525d8c/boltz_client-0.4.0.tar.gz", hash = "sha256:a3f5a6b637350267856e3ab680cd92158de720fa2d5805fc075e4583d020cb2a", size = 5305287, upload-time = "2026-05-18T11:29:50.38Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/be/2a/c99448f0e9789a6d4b6d25392c0eb17b1dedef7c16bafae9a91b2a92e592/boltz_client-0.4.0-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:ed0b520209cf1b05a8523002f5d8f26fa29c04d2faecc9cbde3621b4ddc417e6", size = 5310491, upload-time = "2026-05-18T11:29:48.187Z" },
]
[[package]] [[package]]
name = "breez-sdk" name = "breez-sdk"
version = "0.8.0" version = "0.8.0"
@@ -440,11 +431,11 @@ wheels = [
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2026.5.20" version = "2026.4.22"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
] ]
[[package]] [[package]]
@@ -626,55 +617,55 @@ wheels = [
[[package]] [[package]]
name = "coverage" name = "coverage"
version = "7.14.1" version = "7.14.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } sdist = { url = "https://files.pythonhosted.org/packages/23/7f/d0720730a397a999ffc0fd3f5bebef347338e3a47b727da66fbb228e2ff2/coverage-7.14.0.tar.gz", hash = "sha256:057a6af2f160a85384cde4ab36f0d2777bae1057bae255f95413cdd382aa5c74", size = 919489, upload-time = "2026-05-10T18:02:31.397Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/92/69/0d2ef01ff4b8fcecd4cba920d11e92fa4f96ae412441d3b56a90a258e69b/coverage-7.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e3680291c4a1d0dadfa84a2c459576a4af5133abb617905714339a0c73138cf", size = 219722, upload-time = "2026-05-26T20:38:14.002Z" }, { url = "https://files.pythonhosted.org/packages/59/9d/7c83ef51c3eb495f10010094e661833588b7709946da634c8b66520b97c7/coverage-7.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84c32d90bf4537f0e7b4dec9aaa9a938fb8205136b9d2ecf4d7629d5262dc075", size = 219668, upload-time = "2026-05-10T17:59:23.106Z" },
{ url = "https://files.pythonhosted.org/packages/f8/ae/9afdeaa31b9d9ce98124b6abf8bb49119bf71aecae04f8567c189d91299f/coverage-7.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a5274669f37f2343635a347b91a60777621341ab3378e9c6ac9335eee704bddf", size = 220240, upload-time = "2026-05-26T20:38:17.424Z" }, { url = "https://files.pythonhosted.org/packages/24/34/898546aefbd28f0af131201d0dc852c9e976f817bd7d5bfb8dc4e02863bb/coverage-7.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7c843572c605ab51cfdb5c6b5f2586e2a8467c0d28eca4bdef4ec70c5fecbd82", size = 220192, upload-time = "2026-05-10T17:59:26.095Z" },
{ url = "https://files.pythonhosted.org/packages/51/69/c998589871df7ea7dba865cc5ee32b5a3e1d47ba6c68ef91104c7c46fa5e/coverage-7.14.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cfe5a5fec635799ef33428f1e5e61bafa45a92a96190ba731561ba558ccc214d", size = 246981, upload-time = "2026-05-26T20:38:19.266Z" }, { url = "https://files.pythonhosted.org/packages/df/4a/b457c88aca72b0df13a98167ebd5d947135ccd9881ea88ce6a570e13aa9b/coverage-7.14.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0c451757d3fa2603354fdc789b5e58a0e327a117c370a40e3476ba4eabab228c", size = 246932, upload-time = "2026-05-10T17:59:27.806Z" },
{ url = "https://files.pythonhosted.org/packages/fc/10/1c7d04c13040dac531d21b712bbe08f902e6dd9b58f5d77875c4d030f8f2/coverage-7.14.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:62a9f70b52e0b5a95cfef4a5c5641b06983cadc5e538a3feeb5c00211f523ac2", size = 248812, upload-time = "2026-05-26T20:38:20.75Z" }, { url = "https://files.pythonhosted.org/packages/b5/d9/92600e89486fd074c50f0117422b2c9592c3e144e2f25bd5ac0bc62bc7a0/coverage-7.14.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3fd43f0616e765ab78d069cf8358def7363957a45cee446d65c502dcfeea7893", size = 248762, upload-time = "2026-05-10T17:59:29.479Z" },
{ url = "https://files.pythonhosted.org/packages/c1/65/2a38a4607ef27cadcfbcee034dba5830ae2569f90144a0f4c7dbf47d30b0/coverage-7.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c18ebc343e15be53049b3a2dce38fe82d58f37e20ab9094b3a39c0aa4f6bb47", size = 250675, upload-time = "2026-05-26T20:38:22.159Z" }, { url = "https://files.pythonhosted.org/packages/0d/e1/9ea1eb9c311da7f15853559dc1d9d82bef88ecd3e59fbeb51f16bc2ffa91/coverage-7.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:731e535b1498b27d13594a0527a79b0510867b0ad891532be41cb883f2128e20", size = 250625, upload-time = "2026-05-10T17:59:31.33Z" },
{ url = "https://files.pythonhosted.org/packages/c9/a2/a446ed9752a4a59b79e0fb6cbb319f6facb2183045c0725462625e66f87e/coverage-7.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b84ffdf877644e7096aa936991efeed873f7f3df57b9cd001312b7668ab08550", size = 252590, upload-time = "2026-05-26T20:38:23.63Z" }, { url = "https://files.pythonhosted.org/packages/a5/03/57afca1b8106f8549a5329139315041fe166d6099bd9381346b9430dfbd1/coverage-7.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c7492f2d493b976941c7ca050f273cbda2f43c381124f7586a3e3c16d1804fec", size = 252539, upload-time = "2026-05-10T17:59:32.692Z" },
{ url = "https://files.pythonhosted.org/packages/9e/fd/e81fbd7ba752365546e9842b1cbdaad3d6919d2a522c590aef16a281ec5e/coverage-7.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e854312c4103f2ad4c0dc023b69b77ebfd2c89db5f86c4c94dc2353f9a92167e", size = 247691, upload-time = "2026-05-26T20:38:25.057Z" }, { url = "https://files.pythonhosted.org/packages/57/5e/2e9fc63c9928119c1dbae02222be51407d3e7ebac5811ebbda4af3557795/coverage-7.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc38367eaa2abb1b766ac333142bce7655335a73537f5c8b75aaa89c2b987757", size = 247636, upload-time = "2026-05-10T17:59:34.599Z" },
{ url = "https://files.pythonhosted.org/packages/53/35/f3c26fdaae9ea937d154ca4d372e5ea0a4167ff70d36c6074ac2eacb2f83/coverage-7.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c643734307300234fafa36bf2a040a7235f8f177ea1fd6ec1423aea6fb7b929f", size = 248716, upload-time = "2026-05-26T20:38:26.406Z" }, { url = "https://files.pythonhosted.org/packages/f0/e2/0b7898cda21041cc67546e19b80ba66cbbb47cbece52a76a5904de6a3aaf/coverage-7.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0a951308cde22cf77f953955a754d04dccb57fe3bb8e345d685778ed9fc1632a", size = 248666, upload-time = "2026-05-10T17:59:36.232Z" },
{ url = "https://files.pythonhosted.org/packages/2e/14/940b6c49551fd343e8507ee2b0ba7af5d0aa04ed5bf768285cb7c72a9884/coverage-7.14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84ac9499e48700399a5dd0ea7085b5091961fec52c68d66b4ec0d3cf7f4441b1", size = 246721, upload-time = "2026-05-26T20:38:28.282Z" }, { url = "https://files.pythonhosted.org/packages/d6/e3/d33662a2fdaef23229c15921f39c84ec38441f3069ba26e134ed402c833b/coverage-7.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fab3877e4ebb06bd9d4d4d00ee53309ee5478e66873c66a382272e3ee33eb7ea", size = 246670, upload-time = "2026-05-10T17:59:38.029Z" },
{ url = "https://files.pythonhosted.org/packages/aa/2c/40fc0634186c28292a662dff578866b3913983d6c375a3c2a74020938719/coverage-7.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7f02d09f70776579b926d889a4c9c235070a1f47c40458aeaca563fae5acfdb5", size = 250533, upload-time = "2026-05-26T20:38:29.753Z" }, { url = "https://files.pythonhosted.org/packages/99/b2/533942c3bfbf6770b5c32d7f2ff029fe013dba31f3fe8b45cabbb250365e/coverage-7.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b812eb847b19876ebf33fb6c4f11819af05ab6050b0bfa1bc53412ae81779adb", size = 250484, upload-time = "2026-05-10T17:59:39.974Z" },
{ url = "https://files.pythonhosted.org/packages/de/e3/2c26bf1e811f9df991ff2a9bdddebdd13ee0665d564df7d05979f9146297/coverage-7.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ce66d8e46da2bb5ee313a745cbd2e391d319176c1f7a9451bfcd3a2fb920859b", size = 246990, upload-time = "2026-05-26T20:38:31.516Z" }, { url = "https://files.pythonhosted.org/packages/d8/00/15acbad83a96de13c73831486c7627bfed73dfaec53b04e4a6315edf3fd8/coverage-7.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d9c8ef6ed820c433de075657d72dda1f89a2984955e58b8a75feb3f184250218", size = 246942, upload-time = "2026-05-10T17:59:41.659Z" },
{ url = "https://files.pythonhosted.org/packages/a8/b0/060260ef56bd92363ebdce0c7095ce422b06e69aae71828efeca473ab1ca/coverage-7.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c912c259304cfb5ee584481cfb7ce1ff932b4d61e6c9140b8f19cb7b5ed82332", size = 247593, upload-time = "2026-05-26T20:38:33.065Z" }, { url = "https://files.pythonhosted.org/packages/70/db/cef0228de493f2c740c760a9057a61d00c6849480073b70a75b87c7d4bab/coverage-7.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d128b1bba9361fbaaf6a19e179e6cfd6a9103ce0c0555876f72780acc93efd85", size = 247544, upload-time = "2026-05-10T17:59:43.471Z" },
{ url = "https://files.pythonhosted.org/packages/63/f3/501502046efeb0d6d94b5ca54941d95f1184183dd6bdb7f283985783bb4a/coverage-7.14.1-cp310-cp310-win32.whl", hash = "sha256:1238cb94638e610e972c60dac68e813f868dc7d6e982535270558443058d9d59", size = 222330, upload-time = "2026-05-26T20:38:35.36Z" }, { url = "https://files.pythonhosted.org/packages/77/a0/d9ef8e148f3025c2ae8401d77cda1502b6d2a4d8102603a8af31460aedb6/coverage-7.14.0-cp310-cp310-win32.whl", hash = "sha256:65f267ca1370726ec2c1aa38bbe4df9a71a740f22878d2d4bf59d71a4cd8d323", size = 222285, upload-time = "2026-05-10T17:59:44.908Z" },
{ url = "https://files.pythonhosted.org/packages/a0/5d/1bf99f2c558f128faf7906817ccbdb576ba815d3b41ce2ac1719b70a3663/coverage-7.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253", size = 223261, upload-time = "2026-05-26T20:38:37.196Z" }, { url = "https://files.pythonhosted.org/packages/85/c0/30c454c7d3cf47b2805d4e06f12443f5eece8a5d030d3b0350e7b74ecb49/coverage-7.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:b34ece8065914f938ed7f2c5872bb865336977a52919149846eac3744327267a", size = 223215, upload-time = "2026-05-10T17:59:46.779Z" },
{ url = "https://files.pythonhosted.org/packages/7d/d7/477ad149490e6cb849f28abea1dabb9c823cea72e7500c81b4240ce619c0/coverage-7.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f", size = 219848, upload-time = "2026-05-26T20:38:38.715Z" }, { url = "https://files.pythonhosted.org/packages/fc/e4/649c8d4f7f1709b6dbfc474358aa1bba02f67bcd52e2fec291a5014006cd/coverage-7.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a78e2a9d9c5e3b8d4ab9b9d28c985ea66fced0a7d7c2aec1f216e03a2011480", size = 219795, upload-time = "2026-05-10T17:59:48.198Z" },
{ url = "https://files.pythonhosted.org/packages/91/82/a5eb47257c50601bb7b9a9d2857c67b7a3a85ad74180eb2c98bb1fbe0ce5/coverage-7.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4", size = 220354, upload-time = "2026-05-26T20:38:40.232Z" }, { url = "https://files.pythonhosted.org/packages/7f/8d/46692d24b3f395d4cbf17bfcc57136b4f2f9c0c0df864b0bddfc1d71a014/coverage-7.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1816c505187592dcd1c5a5f226601a549f70365fbd00930ac88b0c225b76bb4", size = 220299, upload-time = "2026-05-10T17:59:49.683Z" },
{ url = "https://files.pythonhosted.org/packages/43/8b/78419b5391a5cb706b6544390507e469d83ffc9a8248b02c4011aceb9365/coverage-7.14.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1", size = 250771, upload-time = "2026-05-26T20:38:41.782Z" }, { url = "https://files.pythonhosted.org/packages/12/c2/a40f5cb295bbcbb697a76947a56081c494c61950366294ee426ffe261099/coverage-7.14.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d8e1762f0e9cbc26ec315471e7b47855218e833cd5a032d706fbf43845d878c7", size = 250721, upload-time = "2026-05-10T17:59:51.494Z" },
{ url = "https://files.pythonhosted.org/packages/77/63/e77aaacd491182210d639636b7a8bba23ffffa9b82aa3762da9431855fa9/coverage-7.14.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f", size = 252683, upload-time = "2026-05-26T20:38:43.305Z" }, { url = "https://files.pythonhosted.org/packages/fd/35/202235eb5c3c14c212462cd91d61b7386bf8fc44bc7a77f4742d2a69174b/coverage-7.14.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9336e23e8bb3a3925398261385e2a1533957d3e760e91070dcb0e98bfa514eed", size = 252633, upload-time = "2026-05-10T17:59:53.244Z" },
{ url = "https://files.pythonhosted.org/packages/65/1c/a022e3cfbec2ac241640003cb3a817e161d9c7f5aa9b49173756cdc03204/coverage-7.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129", size = 254791, upload-time = "2026-05-26T20:38:45.361Z" }, { url = "https://files.pythonhosted.org/packages/bb/80/5f596e8995785124ee191c42535664c5e62c65995b66f4ca21e28ae04c81/coverage-7.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd1169b2230f9cbe9c638ba38022ed7a2b1e641cc07f7cea0365e4be2a74980", size = 254743, upload-time = "2026-05-10T17:59:55.021Z" },
{ url = "https://files.pythonhosted.org/packages/61/d6/967e408aca4c1ceb88cb0cc677169110ae7f5995fb5eaf5fb1f5a1bb8f5d/coverage-7.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860", size = 256748, upload-time = "2026-05-26T20:38:46.91Z" }, { url = "https://files.pythonhosted.org/packages/1e/6d/0d178825be2350f0adb27984d0aa7cf84bbdab201f6fb926b535d23a8f5f/coverage-7.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1bb3543b58fea74d2cd1abc4054cc927e4724687cb4560cd2ed88d2c7d820c0", size = 256700, upload-time = "2026-05-10T17:59:56.511Z" },
{ url = "https://files.pythonhosted.org/packages/b8/be/869188f7fe28638078ec479331ace6dc5f7b40b7153eb616f47ab79404d8/coverage-7.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c", size = 250907, upload-time = "2026-05-26T20:38:48.493Z" }, { url = "https://files.pythonhosted.org/packages/19/5b/9e549c2f6e9dfea472adadba06c294e64735dabc2dd19015fac082095013/coverage-7.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a93bac2cb577ef60074999ed56d8a1535894398e2ed920d4185c3ec0c8864742", size = 250854, upload-time = "2026-05-10T17:59:57.94Z" },
{ url = "https://files.pythonhosted.org/packages/07/aa/adb7d3b4278d690e68703abcd76ab1b948242e3668d921711551b78f9ddb/coverage-7.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7", size = 252483, upload-time = "2026-05-26T20:38:50.074Z" }, { url = "https://files.pythonhosted.org/packages/3d/1c/b94f9f5f36396021ee2f62c5834b12e6a3d31f0bed5d6fc6d1c3caec087c/coverage-7.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5904abf7e18cddc463219b17552229650c6b79e061d31a1059283051169cf7d5", size = 252433, upload-time = "2026-05-10T17:59:59.688Z" },
{ url = "https://files.pythonhosted.org/packages/43/61/331c74103c62dcb0c4b9b3a0de9a61aca016208b0a90f109592a9f9ecc28/coverage-7.14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec", size = 250545, upload-time = "2026-05-26T20:38:51.613Z" }, { url = "https://files.pythonhosted.org/packages/b5/cb/d192cd8e1345eccabc32016f2d39072ecd10cb4f4b983ed8d0ebdeaf00dc/coverage-7.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:741f57cddc9004a8c81b084660215f33a6b597dbe62c31386b983ee26310e327", size = 250494, upload-time = "2026-05-10T18:00:01.953Z" },
{ url = "https://files.pythonhosted.org/packages/f6/b6/c5dae3c104d89be04828f61810e6b3473825482e4c288cc4ed04553e08ae/coverage-7.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef", size = 254310, upload-time = "2026-05-26T20:38:53.503Z" }, { url = "https://files.pythonhosted.org/packages/53/c5/aac9f460a41d835dbddef1d377f105f6ac2311d0f3c1588e9f51046d8813/coverage-7.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:664123feb0929d7affc135717dbd70d61d98688a08ab1e5ba464739620c6252d", size = 254261, upload-time = "2026-05-10T18:00:03.779Z" },
{ url = "https://files.pythonhosted.org/packages/ad/a1/2b9d5863e3b83c01ad8199e3c597802fbb3a9dc90b058885804c20296d31/coverage-7.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df", size = 250266, upload-time = "2026-05-26T20:38:55.414Z" }, { url = "https://files.pythonhosted.org/packages/23/aa/7af7c0081980a9cb3d289c5a435a4b7657dcecbd128e25c580e6a50389b5/coverage-7.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c83d2399a51bbec8429266905d33616f04bc5726b1138c35844d5fcd896b2e20", size = 250216, upload-time = "2026-05-10T18:00:05.262Z" },
{ url = "https://files.pythonhosted.org/packages/7f/5e/0e511fbdb269359be26fe678a1c3fa1f2aa2a01573cc3f54268c8d6d4797/coverage-7.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9", size = 251174, upload-time = "2026-05-26T20:38:57.141Z" }, { url = "https://files.pythonhosted.org/packages/35/60/a4257538ce2f6b978aeb51870d6c4208c510928a03db7e0339bb625dccb7/coverage-7.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb2e855b87321259a037429288ae85216d191c74de3e79bf57cd2bc0761992c", size = 251125, upload-time = "2026-05-10T18:00:06.858Z" },
{ url = "https://files.pythonhosted.org/packages/85/10/e55307b622b3dd9671cb321824502dc10f93e72f2802b9946159a8edadeb/coverage-7.14.1-cp311-cp311-win32.whl", hash = "sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548", size = 222354, upload-time = "2026-05-26T20:38:58.727Z" }, { url = "https://files.pythonhosted.org/packages/a1/ab/f91af47642ec1aa53490e835a95847168d9c77fc39aa58527604c051e145/coverage-7.14.0-cp311-cp311-win32.whl", hash = "sha256:731dc15b385ac52289743d476245b61e1a2927e803bef655b52bc3b2a75a21f3", size = 222300, upload-time = "2026-05-10T18:00:08.608Z" },
{ url = "https://files.pythonhosted.org/packages/71/cf/107421693cfb71e4f1ca5bf70443f64d4161878068d07a3e51c7ad21d17b/coverage-7.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e", size = 223290, upload-time = "2026-05-26T20:39:00.413Z" }, { url = "https://files.pythonhosted.org/packages/f0/f0/a71ddbd874431e7a7cd96071f0c331cfbbad07704833c765d24ffbab8a67/coverage-7.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfb0ed8ec5d25e93face268115d7964db9df8b9aae8edcde9ec6b16c726a7cc1", size = 223241, upload-time = "2026-05-10T18:00:10.746Z" },
{ url = "https://files.pythonhosted.org/packages/b8/1d/3e3644585eb29e9dafefb19555078529a4d7cce12bd21929664eea989277/coverage-7.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3", size = 221953, upload-time = "2026-05-26T20:39:02.159Z" }, { url = "https://files.pythonhosted.org/packages/d8/6e/d9d312a5151a96cd110efee32efc3fc97b01ebd86203fe618ccb29cf4c92/coverage-7.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:7ebb1c6df9f78046a1b1e0a89674cd4bf73b7c648914eebcf976a57fd99a5627", size = 221908, upload-time = "2026-05-10T18:00:12.242Z" },
{ url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, { url = "https://files.pythonhosted.org/packages/09/1e/2f996b2c8415cbb6f54b0f5ec1ee850c96d7911961afb4fc05f4a89d8c58/coverage-7.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7ffd19fc8aed057fd686a17a4935eef5f9859d69208f96310e893e64b9b6ccf5", size = 219967, upload-time = "2026-05-10T18:00:13.756Z" },
{ url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, { url = "https://files.pythonhosted.org/packages/34/23/35c7aea1274aef7525bdd2dc92f710bdde6d11652239d71d1ec450067939/coverage-7.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:829994cfe1aeb773ca27bf246d4badc1e764893e3bfb98fff820fcecd1ca4662", size = 220329, upload-time = "2026-05-10T18:00:15.264Z" },
{ url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, { url = "https://files.pythonhosted.org/packages/75/cf/a8f4b43a16e194b0261257ad28ded5853ec052570afef4a84e1d81189f3b/coverage-7.14.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b4f07cf7edcb7ec39431a5074d7ea83b29a9f71fcfc494f0f40af4e65180420f", size = 251839, upload-time = "2026-05-10T18:00:17.16Z" },
{ url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, { url = "https://files.pythonhosted.org/packages/69/ff/6699e7b71e60d3049eb2bdcbc95ee3f35707b2b0e48f32e9e63d3ce30c08/coverage-7.14.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca3d9cf2c32b521bd9518385608787fa86f38daf993695307531822c3430ed67", size = 254576, upload-time = "2026-05-10T18:00:18.829Z" },
{ url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, { url = "https://files.pythonhosted.org/packages/22/ec/c936d495fcd67f48f03a9c4ad3297ff80d1f222a5df3980f15b34c186c21/coverage-7.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92af52828e7f29d827346b0294e5a0853fa206db77db0395b282918d41e28db9", size = 255690, upload-time = "2026-05-10T18:00:20.648Z" },
{ url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, { url = "https://files.pythonhosted.org/packages/5c/42/5af63f636cc62a4a2b1b3ba9146f6ee6f53a35a50d5cefc54d5670f60999/coverage-7.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7b2bb6c9d7e769360d0f20a0f219603fd64f0c8f97de17ab25853261602be0fb", size = 257949, upload-time = "2026-05-10T18:00:22.28Z" },
{ url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, { url = "https://files.pythonhosted.org/packages/26/d3/a225317bd2012132a27e1176d51660b826f99bb975876463c44ea0d7ee5a/coverage-7.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1c9ed6ef99f88fb8c14aa8e2bf8eb0fe55fa2edfea68f8675d78741df1a5ac0e", size = 252242, upload-time = "2026-05-10T18:00:24.076Z" },
{ url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, { url = "https://files.pythonhosted.org/packages/f1/7f/9e65495298c3ea414742998539c37d048b5e81cc818fb1828cc6b51d10bf/coverage-7.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8231ade007f37959fbf58acc677f26b922c02eda6f0428ea307da0fd39681bf3", size = 253608, upload-time = "2026-05-10T18:00:25.588Z" },
{ url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, { url = "https://files.pythonhosted.org/packages/94/46/1522b524a35bdad22b2b8c4f9d32d0a104b524726ec380b2db68db1746f5/coverage-7.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8b013632cc1ce1d09dbe4f32667b4d320ec2f54fc326ebeffcd0b0bcc2bb6c4", size = 251753, upload-time = "2026-05-10T18:00:27.104Z" },
{ url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, { url = "https://files.pythonhosted.org/packages/f3/e9/cdf00d38817742c541ade405e115a3f7bf36e6f2a8b99d4f209861b85a2d/coverage-7.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1733198802d71ec4c524f322e2867ee05c62e9e75df86bdca545407a221827d1", size = 255823, upload-time = "2026-05-10T18:00:29.038Z" },
{ url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, { url = "https://files.pythonhosted.org/packages/38/fc/5e7877cf5f902d08a17ff1c532511476d87e1bea355bd5028cb97f902e79/coverage-7.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:72a305291fa8ee01332f1aaf38b348ca34097f6aa0b0ef627eef2837e57bbba5", size = 251323, upload-time = "2026-05-10T18:00:30.647Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, { url = "https://files.pythonhosted.org/packages/18/9d/50f05a72dff8487464fdd4178dda5daed642a060e60afb644e3d45123559/coverage-7.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fcaba850dd317c65423a9d63d88f9573c53b00354d6dd95724576cc98a131595", size = 253197, upload-time = "2026-05-10T18:00:32.211Z" },
{ url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, { url = "https://files.pythonhosted.org/packages/00/3f/6f61ffe6439df266c3cf60f5c99cfaa21103d0210d706a42fc6c30683ff8/coverage-7.14.0-cp312-cp312-win32.whl", hash = "sha256:5ac83957a80d0701310e96d8bec68cdcf4f90a7674b7d13f15a344315b41ab27", size = 222515, upload-time = "2026-05-10T18:00:33.717Z" },
{ url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, { url = "https://files.pythonhosted.org/packages/85/19/93853133df2cb371083285ef6a93982a0173e7a233b0f61373ba9fd30eb2/coverage-7.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:70390b0da32cb90b501953716302906e8bcce087cb283e70d8c97729f22e92b2", size = 223324, upload-time = "2026-05-10T18:00:35.172Z" },
{ url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, { url = "https://files.pythonhosted.org/packages/74/18/9f7fe62f659f24b7a82a0be56bf94c1bd0a89e0ae7ab4c668f6e82404294/coverage-7.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:91b993743d959b8be85b4abf9d5478216a69329c321efe5be0433c1a841d691d", size = 221944, upload-time = "2026-05-10T18:00:37.014Z" },
{ url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, { url = "https://files.pythonhosted.org/packages/61/e8/cb8e80d6f9f55b99588625062822bf946cf03ed06315df4bd8397f5632a1/coverage-7.14.0-py3-none-any.whl", hash = "sha256:8de5b61163aee3d05c8a2beab6f47913df7981dad1baf82c414d99158c286ab1", size = 211764, upload-time = "2026-05-10T18:02:29.538Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@@ -1084,11 +1075,11 @@ wheels = [
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.16" version = "3.14"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" } sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, { url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" },
] ]
[[package]] [[package]]
@@ -1123,59 +1114,59 @@ wheels = [
[[package]] [[package]]
name = "jiter" name = "jiter"
version = "0.15.0" version = "0.14.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } sdist = { url = "https://files.pythonhosted.org/packages/6e/c1/0cddc6eb17d4c53a99840953f95dd3accdc5cfc7a337b0e9b26476276be9/jiter-0.14.0.tar.gz", hash = "sha256:e8a39e66dac7153cf3f964a12aad515afa8d74938ec5cc0018adcdae5367c79e", size = 165725, upload-time = "2026-04-10T14:28:42.01Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/da/76a2c7e510ba15fe323d9509c223ab272da79ea59f54488f4a78da6426db/jiter-0.15.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4", size = 310849, upload-time = "2026-05-19T10:06:51.944Z" }, { url = "https://files.pythonhosted.org/packages/64/2e/a9959997739c403378d0a4a3a1c4ed80b60aeace216c4d37b303a9fc60a4/jiter-0.14.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:02f36a5c700f105ac04a6556fe664a59037a2c200db3b7e88784fac2ddf02531", size = 316927, upload-time = "2026-04-10T14:25:40.753Z" },
{ url = "https://files.pythonhosted.org/packages/5d/8e/827be942883a4dc0862c48626ff41af3320b1902d136a0bf4b9041f2c567/jiter-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f", size = 314991, upload-time = "2026-05-19T10:06:53.522Z" }, { url = "https://files.pythonhosted.org/packages/27/72/b6de8a531e0adbadd839bec301165feb1fccf00e9ff55073ba2dd20f0043/jiter-0.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41eab6c09ceffb6f0fe25e214b3068146edb1eda3649ca2aee2a061029c7ba2e", size = 321181, upload-time = "2026-04-10T14:25:42.621Z" },
{ url = "https://files.pythonhosted.org/packages/6d/38/be2832be361ba1b9517c76f46d30b64e985be1dd43c974f4c3a4b1844436/jiter-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18", size = 340843, upload-time = "2026-05-19T10:06:55.071Z" }, { url = "https://files.pythonhosted.org/packages/db/d8/2040b9efa13c917f855c40890ae4119fe02c25b7c7677d5b4fa820a851fc/jiter-0.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cf4d4c109641f9cfaf4a7b6aebd51654e405cd00fa9ebbf87163b8b97b325aa", size = 347387, upload-time = "2026-04-10T14:25:44.212Z" },
{ url = "https://files.pythonhosted.org/packages/6d/d8/90f01fb83c0c7ba509303ec93e32a308fbfa167d264860b01c0fd0dbbd06/jiter-0.15.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f", size = 365116, upload-time = "2026-05-19T10:06:56.893Z" }, { url = "https://files.pythonhosted.org/packages/49/62/655c0ad5ce6a8e90f9068c175b8a236877d753e460762b3183c136db1c5b/jiter-0.14.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80c7b41a628e6be2213ad0ece763c5f88aa5ee003fa394d58acaaee1f4b8342", size = 373083, upload-time = "2026-04-10T14:25:45.55Z" },
{ url = "https://files.pythonhosted.org/packages/91/38/94593d34f8c67a0b6f6cbc027f016ffa9780b3a858a7a86f6fd7a15bcc1e/jiter-0.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4", size = 457970, upload-time = "2026-05-19T10:06:58.707Z" }, { url = "https://files.pythonhosted.org/packages/f1/66/549c40fa068f08710b7570869c306a051eb67a29758bd64f4114f730554c/jiter-0.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb3dbf7cc0d4dbe73cce307ebe7eefa7f73a7d3d854dd119ea0c243f03e40927", size = 463639, upload-time = "2026-04-10T14:25:47.452Z" },
{ url = "https://files.pythonhosted.org/packages/df/04/d79962dd49d00c97e2a9b4cacea1947904d02135936960351f9a96d4c1a6/jiter-0.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6", size = 375744, upload-time = "2026-05-19T10:07:00.471Z" }, { url = "https://files.pythonhosted.org/packages/25/2f/97a32a05fed14ed58a18e181fdfb619e05163f3726b54ee6080ec0539c09/jiter-0.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7054adcdeb06b46efd17b5734f75817a44a2d06d3748e36c3a023a1bb52af9ec", size = 380735, upload-time = "2026-04-10T14:25:49.305Z" },
{ url = "https://files.pythonhosted.org/packages/c3/2e/5d37abe2be0e819c21e2338bebd410e481763ce526a9138c8c3652fa0123/jiter-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c", size = 349609, upload-time = "2026-05-19T10:07:01.829Z" }, { url = "https://files.pythonhosted.org/packages/2a/3b/4347e1d6c2a973d653bbb7a2d671a2d2426e54b52ba735b8ff0d0a29b75c/jiter-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d597cd1bf6790376f3fffc7c708766e57301d99a19314824ea0ccc9c3c70e1e2", size = 358632, upload-time = "2026-04-10T14:25:50.931Z" },
{ url = "https://files.pythonhosted.org/packages/7a/90/98768ad2ed90c1fda15d64157de2dfbf73c1c074d4b1bfaca915480bc7cf/jiter-0.15.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512", size = 354366, upload-time = "2026-05-19T10:07:03.587Z" }, { url = "https://files.pythonhosted.org/packages/ef/24/ca452fbf2ea33548ed30ce68a39a50442d3f7c9bf0704a7af958a930c057/jiter-0.14.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:df63a14878da754427926281626fd3ee249424a186e25a274e78176d42945264", size = 359969, upload-time = "2026-04-10T14:25:52.381Z" },
{ url = "https://files.pythonhosted.org/packages/d6/c4/fbfb806209f1fe4b7dccdfb07bc62bb044300734a945b06fd64db446ef6a/jiter-0.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a", size = 393519, upload-time = "2026-05-19T10:07:05.08Z" }, { url = "https://files.pythonhosted.org/packages/e3/a3/94470a0d199287caabeb4da2bb2ae5f6d17f3cf05dfc975d7cb064d58e0f/jiter-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ea73187627bcc5810e085df715e8a99da8bdfd96a7eb36b4b4df700ba6d4c9c", size = 397529, upload-time = "2026-04-10T14:25:53.801Z" },
{ url = "https://files.pythonhosted.org/packages/37/1c/b9c257cd70cb453b6d10f3ebf0402cdb11669ab455389096f09839670290/jiter-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887", size = 519952, upload-time = "2026-05-19T10:07:06.589Z" }, { url = "https://files.pythonhosted.org/packages/cf/71/6768edc09d7c45c39f093feb3de105fa718a3e982b5208b8a2ed6382b44b/jiter-0.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9f541eaf7bb8382367a1a23d6fc3d6aad57f8dd8c18c3c17f838bee20f217220", size = 522342, upload-time = "2026-04-10T14:25:55.396Z" },
{ url = "https://files.pythonhosted.org/packages/a9/1a/aa85027db7ab15829c12feebbc33b404f53fc399bd559d85fd0d6365ff0d/jiter-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823", size = 550770, upload-time = "2026-05-19T10:07:08.228Z" }, { url = "https://files.pythonhosted.org/packages/3d/6b/5c2e17559a0f4e96e934479f7137df46c939e983fa05244e674815befb73/jiter-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:107465250de4fce00fdb47166bcd51df8e634e049541174fe3c71848e44f52ce", size = 556784, upload-time = "2026-04-10T14:25:56.927Z" },
{ url = "https://files.pythonhosted.org/packages/d4/54/8c3f65c8a5687925e84708f19d63f7f37d28e2b86a48d951702ad94424d8/jiter-0.15.0-cp310-cp310-win32.whl", hash = "sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53", size = 209303, upload-time = "2026-05-19T10:07:10.006Z" }, { url = "https://files.pythonhosted.org/packages/b1/83/c25f3556a60fc74d11199100f1b6cc0c006b815c8494dea8ca16fe398732/jiter-0.14.0-cp310-cp310-win32.whl", hash = "sha256:ffb2a08a406465bb076b7cc1df41d833106d3cf7905076cc73f0cb90078c7d10", size = 208439, upload-time = "2026-04-10T14:25:58.796Z" },
{ url = "https://files.pythonhosted.org/packages/d5/72/0528a1eb9f42dd2d8228a0711458628f35924d131f623eaebc35fd23d3d4/jiter-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1", size = 200404, upload-time = "2026-05-19T10:07:11.426Z" }, { url = "https://files.pythonhosted.org/packages/2e/99/781a1b413f0989b7f2ea203b094b331685f1a35e52e0a45e5d000ecaab27/jiter-0.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb8b682d10cb0cce7ff4c1af7244af7022c9b01ae16d46c357bdd0df13afb25d", size = 204558, upload-time = "2026-04-10T14:26:00.208Z" },
{ url = "https://files.pythonhosted.org/packages/e4/13/daa722f5765c393576f466378f9dfd29d77c9bed939e0688f96afa3601ea/jiter-0.15.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2", size = 310899, upload-time = "2026-05-19T10:07:12.89Z" }, { url = "https://files.pythonhosted.org/packages/8a/1f/198ae537fccb7080a0ed655eb56abf64a92f79489dfbf79f40fa34225bcd/jiter-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7e791e247b8044512e070bd1f3633dc08350d32776d2d6e7473309d0edf256a2", size = 316896, upload-time = "2026-04-10T14:26:01.986Z" },
{ url = "https://files.pythonhosted.org/packages/7f/82/2d2551829b082f4b6d82b9f939b031fb808a10aab1ec0664f82e150bb9a2/jiter-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67", size = 314963, upload-time = "2026-05-19T10:07:14.539Z" }, { url = "https://files.pythonhosted.org/packages/cf/34/da67cff3fce964a36d03c3e365fb0f8726ade2a6cfd4d3c70107e216ead6/jiter-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71527ce13fd5a0c4e40ad37331f8c547177dbb2dd0a93e5278b6a5eecf748804", size = 321085, upload-time = "2026-04-10T14:26:03.364Z" },
{ url = "https://files.pythonhosted.org/packages/2a/0a/8b1a51466f7fe9f31dbe4bc7e0ca848674f9825e0f737b929b97e8c60aa7/jiter-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a", size = 341730, upload-time = "2026-05-19T10:07:15.869Z" }, { url = "https://files.pythonhosted.org/packages/ed/36/4c72e67180d4e71a4f5dcf7886d0840e83c49ab11788172177a77570326e/jiter-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c4a7ab56f746014874f2c525584c0daca1dec37f66fd707ecef3b7e5c2228c", size = 347393, upload-time = "2026-04-10T14:26:05.314Z" },
{ url = "https://files.pythonhosted.org/packages/f6/2a/e71dea19822e2e404e83992a08c1d6b9b617bb944f28c9c2fbd85d02c91e/jiter-0.15.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7", size = 366214, upload-time = "2026-05-19T10:07:17.259Z" }, { url = "https://files.pythonhosted.org/packages/bc/db/9b39e09ceafa9878235c0fc29e3e3f9b12a4c6a98ea3085b998cadf3accc/jiter-0.14.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:376e9dafff914253bb9d46cdc5f7965607fbe7feb0a491c34e35f92b2770702e", size = 372937, upload-time = "2026-04-10T14:26:06.884Z" },
{ url = "https://files.pythonhosted.org/packages/c4/59/97e1fa539d124a509a00ab7f669289d1c1d236ecabf12948a18f16c91082/jiter-0.15.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd", size = 459527, upload-time = "2026-05-19T10:07:18.741Z" }, { url = "https://files.pythonhosted.org/packages/b0/96/0dcba1d7a82c1b720774b48ef239376addbaf30df24c34742ac4a57b67b2/jiter-0.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23ad2a7a9da1935575c820428dd8d2490ce4d23189691ce33da1fc0a58e14e1c", size = 463646, upload-time = "2026-04-10T14:26:08.345Z" },
{ url = "https://files.pythonhosted.org/packages/d1/7a/4a68d331aef8cf2e2393c14a3aacb635c62aa86071b0229899fb5baaa907/jiter-0.15.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281", size = 375451, upload-time = "2026-05-19T10:07:20.208Z" }, { url = "https://files.pythonhosted.org/packages/f1/e3/f61b71543e746e6b8b805e7755814fc242715c16f1dba58e1cbccb8032c2/jiter-0.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54b3ddf5786bc7732d293bba3411ac637ecfa200a39983166d1df86a59a43c9f", size = 380225, upload-time = "2026-04-10T14:26:10.161Z" },
{ url = "https://files.pythonhosted.org/packages/7b/7e/1c445c2b6f0e30a274dc8082e0c3c7825411cce80d726bccd697c98cc8d3/jiter-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708", size = 349428, upload-time = "2026-05-19T10:07:22.372Z" }, { url = "https://files.pythonhosted.org/packages/ad/5e/0ddeb7096aca099114abe36c4921016e8d251e6f35f5890240b31f1f60ae/jiter-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c001d5a646c2a50dc055dd526dad5d5245969e8234d2b1131d0451e81f3a373", size = 358682, upload-time = "2026-04-10T14:26:11.574Z" },
{ url = "https://files.pythonhosted.org/packages/00/94/e20d38984fc17a636371bffd2ae0f698124fdc8e75ef969cd2da6ba7cea7/jiter-0.15.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928", size = 355405, upload-time = "2026-05-19T10:07:23.916Z" }, { url = "https://files.pythonhosted.org/packages/e9/d1/fe0c46cd7fda9cad8f1ff9ad217dc61f1e4280b21052ec6dfe88c1446ef2/jiter-0.14.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:834bb5bdabca2e91592a03d373838a8d0a1b8bbde7077ae6913fd2fc51812d00", size = 359973, upload-time = "2026-04-10T14:26:13.316Z" },
{ url = "https://files.pythonhosted.org/packages/94/fa/4d09f814779d0ea80a28ed8e4c6662ec9a4a8ecef0ac52190ebac6262d14/jiter-0.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd", size = 393688, upload-time = "2026-05-19T10:07:25.854Z" }, { url = "https://files.pythonhosted.org/packages/ac/21/f5317f91729b501019184771c80d60abd89907009e7bfa6c7e348c5bdd44/jiter-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4e9178be60e229b1b2b0710f61b9e24d1f4f8556985a83ff4c4f95920eea7314", size = 397568, upload-time = "2026-04-10T14:26:15.212Z" },
{ url = "https://files.pythonhosted.org/packages/54/9d/8eb5d4fb8bf7e93a75964a5da71a75c67c864baf7fa3f98598187b3c7e57/jiter-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e", size = 520853, upload-time = "2026-05-19T10:07:27.303Z" }, { url = "https://files.pythonhosted.org/packages/e9/05/79d8f33fb2bf168db0df5c9cd16fe440a8ada57e929d3677b22712c2568f/jiter-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7e4ccff04ec03614e62c613e976a3a5860dc9714ce8266f44328bdc8b1cab2c", size = 522535, upload-time = "2026-04-10T14:26:16.956Z" },
{ url = "https://files.pythonhosted.org/packages/e7/2c/5e07874e59e623a943a0acf1552a80d05b70f31b402287a8fc6d7ec634c7/jiter-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef", size = 551016, upload-time = "2026-05-19T10:07:28.846Z" }, { url = "https://files.pythonhosted.org/packages/5c/00/d1e3ff3d2a465e67f08507d74bafb2dcd29eba91dc939820e39e8dea38b8/jiter-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69539d936fb5d55caf6ecd33e2e884de083ff0ea28579780d56c4403094bb8d9", size = 556709, upload-time = "2026-04-10T14:26:18.5Z" },
{ url = "https://files.pythonhosted.org/packages/22/ed/d2d34422143474cadc15b60d482b1c35683dbc5c63c24346ddd0df09bcaf/jiter-0.15.0-cp311-cp311-win32.whl", hash = "sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32", size = 209518, upload-time = "2026-05-19T10:07:30.431Z" }, { url = "https://files.pythonhosted.org/packages/60/5b/bbb2189f62ace8d95e869aa4c84c9946616f301e2d02895a6f20dcc3bba3/jiter-0.14.0-cp311-cp311-win32.whl", hash = "sha256:4927d09b3e572787cc5e0a5318601448e1ab9391bcef95677f5840c2d00eaa6d", size = 208660, upload-time = "2026-04-10T14:26:20.511Z" },
{ url = "https://files.pythonhosted.org/packages/1d/7d/52778b930e5cc3e52a37d950b1c10494244308b4329b25a0ff0d88303a81/jiter-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04", size = 200565, upload-time = "2026-05-19T10:07:32.125Z" }, { url = "https://files.pythonhosted.org/packages/b8/86/c500b53dcbf08575f5963e536ebd757a1f7c568272ba5d180b212c9a87fb/jiter-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:42d6ed359ac49eb922fdd565f209c57340aa06d589c84c8413e42a0f9ae1b842", size = 204659, upload-time = "2026-04-10T14:26:22.152Z" },
{ url = "https://files.pythonhosted.org/packages/3b/4f/d9b4067feb69b3fa6eb0488e1b59e2ad5b463fe39f59e527eab2aca00bb0/jiter-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865", size = 195488, upload-time = "2026-05-19T10:07:33.846Z" }, { url = "https://files.pythonhosted.org/packages/75/4a/a676249049d42cb29bef82233e4fe0524d414cbe3606c7a4b311193c2f77/jiter-0.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:6dd689f5f4a5a33747b28686e051095beb214fe28cfda5e9fe58a295a788f593", size = 194772, upload-time = "2026-04-10T14:26:23.458Z" },
{ url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, { url = "https://files.pythonhosted.org/packages/5a/68/7390a418f10897da93b158f2d5a8bd0bcd73a0f9ec3bb36917085bb759ef/jiter-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb2ce3a7bc331256dfb14cefc34832366bb28a9aca81deaf43bbf2a5659e607", size = 316295, upload-time = "2026-04-10T14:26:24.887Z" },
{ url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, { url = "https://files.pythonhosted.org/packages/60/a0/5854ac00ff63551c52c6c89534ec6aba4b93474e7924d64e860b1c94165b/jiter-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5252a7ca23785cef5d02d4ece6077a1b556a410c591b379f82091c3001e14844", size = 315898, upload-time = "2026-04-10T14:26:26.601Z" },
{ url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, { url = "https://files.pythonhosted.org/packages/41/a1/4f44832650a16b18e8391f1bf1d6ca4909bc738351826bcc198bba4357f4/jiter-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c409578cbd77c338975670ada777add4efd53379667edf0aceea730cabede6fb", size = 343730, upload-time = "2026-04-10T14:26:28.326Z" },
{ url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, { url = "https://files.pythonhosted.org/packages/48/64/a329e9d469f86307203594b1707e11ae51c3348d03bfd514a5f997870012/jiter-0.14.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ede4331a1899d604463369c730dbb961ffdc5312bc7f16c41c2896415b1304a", size = 370102, upload-time = "2026-04-10T14:26:30.089Z" },
{ url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, { url = "https://files.pythonhosted.org/packages/94/c1/5e3dfc59635aa4d4c7bd20a820ac1d09b8ed851568356802cf1c08edb3cf/jiter-0.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92cd8b6025981a041f5310430310b55b25ca593972c16407af8837d3d7d2ca01", size = 461335, upload-time = "2026-04-10T14:26:31.911Z" },
{ url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, { url = "https://files.pythonhosted.org/packages/e3/1b/dd157009dbc058f7b00108f545ccb72a2d56461395c4fc7b9cfdccb00af4/jiter-0.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:351bf6eda4e3a7ceb876377840c702e9a3e4ecc4624dbfb2d6463c67ae52637d", size = 378536, upload-time = "2026-04-10T14:26:33.595Z" },
{ url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, { url = "https://files.pythonhosted.org/packages/91/78/256013667b7c10b8834f8e6e54cd3e562d4c6e34227a1596addccc05e38c/jiter-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1dcfbeb93d9ecd9ca128bbf8910120367777973fa193fb9a39c31237d8df165", size = 353859, upload-time = "2026-04-10T14:26:35.098Z" },
{ url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, { url = "https://files.pythonhosted.org/packages/de/d9/137d65ade9093a409fe80955ce60b12bb753722c986467aeda47faf450ad/jiter-0.14.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ae039aaef8de3f8157ecc1fdd4d85043ac4f57538c245a0afaecb8321ec951c3", size = 357626, upload-time = "2026-04-10T14:26:36.685Z" },
{ url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, { url = "https://files.pythonhosted.org/packages/2e/48/76750835b87029342727c1a268bea8878ab988caf81ee4e7b880900eeb5a/jiter-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d9d51eb96c82a9652933bd769fe6de66877d6eb2b2440e281f2938c51b5643e", size = 393172, upload-time = "2026-04-10T14:26:38.097Z" },
{ url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, { url = "https://files.pythonhosted.org/packages/a6/60/456c4e81d5c8045279aefe60e9e483be08793828800a4e64add8fdde7f2a/jiter-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d824ca4148b705970bf4e120924a212fdfca9859a73e42bd7889a63a4ea6bb98", size = 520300, upload-time = "2026-04-10T14:26:39.532Z" },
{ url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, { url = "https://files.pythonhosted.org/packages/a8/9f/2020e0984c235f678dced38fe4eec3058cf528e6af36ebf969b410305941/jiter-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff3a6465b3a0f54b1a430f45c3c0ba7d61ceb45cbc3e33f9e1a7f638d690baf3", size = 553059, upload-time = "2026-04-10T14:26:40.991Z" },
{ url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, { url = "https://files.pythonhosted.org/packages/ef/32/e2d298e1a22a4bbe6062136d1c7192db7dba003a6975e51d9a9eecabc4c2/jiter-0.14.0-cp312-cp312-win32.whl", hash = "sha256:5dec7c0a3e98d2a3f8a2e67382d0d7c3ac60c69103a4b271da889b4e8bb1e129", size = 206030, upload-time = "2026-04-10T14:26:42.517Z" },
{ url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, { url = "https://files.pythonhosted.org/packages/36/ac/96369141b3d8a4a8e4590e983085efe1c436f35c0cda940dd76d942e3e40/jiter-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:fc7e37b4b8bc7e80a63ad6cfa5fc11fab27dbfea4cc4ae644b1ab3f273dc348f", size = 201603, upload-time = "2026-04-10T14:26:44.328Z" },
{ url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, { url = "https://files.pythonhosted.org/packages/01/c3/75d847f264647017d7e3052bbcc8b1e24b95fa139c320c5f5066fa7a0bdd/jiter-0.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:ee4a72f12847ef29b072aee9ad5474041ab2924106bdca9fcf5d7d965853e057", size = 191525, upload-time = "2026-04-10T14:26:46Z" },
{ url = "https://files.pythonhosted.org/packages/65/43/1fc62172aa98b50a7de9a25554060db510f85c89cfbed0dfe13e1907a139/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750", size = 305585, upload-time = "2026-05-19T10:09:35.995Z" }, { url = "https://files.pythonhosted.org/packages/32/a1/ef34ca2cab2962598591636a1804b93645821201cc0095d4a93a9a329c9d/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a25ffa2dbbdf8721855612f6dca15c108224b12d0c4024d0ac3d7902132b4211", size = 311366, upload-time = "2026-04-10T14:28:27.943Z" },
{ url = "https://files.pythonhosted.org/packages/e8/c4/dd58fcd9e2df83666e5c1c1347bef58ce919cd8efc3ffa38aeea62ce493b/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b", size = 306936, upload-time = "2026-05-19T10:09:37.435Z" }, { url = "https://files.pythonhosted.org/packages/60/bb/520576a532a6b8a6f42747afed289c8448c879a34d7802fe2c832d4fd38f/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ac9cbaa86c10996b92bd12c91659b60f939f8e28fcfa6bc11a0e90a774ce95b", size = 309873, upload-time = "2026-04-10T14:28:29.688Z" },
{ url = "https://files.pythonhosted.org/packages/39/86/b695e16f1180c07f43ea98e73ecd21cf63fa2e1b0c1103739013784d11ae/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b", size = 342453, upload-time = "2026-05-19T10:09:39.294Z" }, { url = "https://files.pythonhosted.org/packages/b2/7c/c16db114ea1f2f532f198aa8dc39585026af45af362c69a0492f31bc4821/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:844e73b6c56b505e9e169234ea3bdea2ea43f769f847f47ac559ba1d2361ebea", size = 344816, upload-time = "2026-04-10T14:28:31.348Z" },
{ url = "https://files.pythonhosted.org/packages/34/56/55d76614af37fe3f22a3347d1e410d2a15da581997cb2da499a625000bb5/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c", size = 345606, upload-time = "2026-05-19T10:09:40.727Z" }, { url = "https://files.pythonhosted.org/packages/99/8f/15e7741ff19e9bcd4d753f7ff22f988fd54592f134ca13701c13ea8c20e0/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52c076f187405fc21523c746c04399c9af8ece566077ed147b2126f2bcba577", size = 351445, upload-time = "2026-04-10T14:28:33.093Z" },
{ url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, { url = "https://files.pythonhosted.org/packages/21/42/9042c3f3019de4adcb8c16591c325ec7255beea9fcd33a42a43f3b0b1000/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:fbd9e482663ca9d005d051330e4d2d8150bb208a209409c10f7e7dfdf7c49da9", size = 308810, upload-time = "2026-04-10T14:28:34.673Z" },
{ url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, { url = "https://files.pythonhosted.org/packages/60/cf/a7e19b308bd86bb04776803b1f01a5f9a287a4c55205f4708827ee487fbf/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:33a20d838b91ef376b3a56896d5b04e725c7df5bc4864cc6569cf046a8d73b6d", size = 308443, upload-time = "2026-04-10T14:28:36.658Z" },
{ url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, { url = "https://files.pythonhosted.org/packages/ca/44/e26ede3f0caeff93f222559cb0cc4ca68579f07d009d7b6010c5b586f9b1/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:432c4db5255d86a259efde91e55cb4c8d18c0521d844c9e2e7efcce3899fb016", size = 343039, upload-time = "2026-04-10T14:28:38.356Z" },
{ url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, { url = "https://files.pythonhosted.org/packages/da/e9/1f9ada30cef7b05e74bb06f52127e7a724976c225f46adb65c37b1dadfb6/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f00d94b281174144d6532a04b66a12cb866cbdc47c3af3bfe2973677f9861a", size = 349613, upload-time = "2026-04-10T14:28:40.066Z" },
] ]
[[package]] [[package]]
@@ -1284,7 +1275,7 @@ wheels = [
[[package]] [[package]]
name = "lnbits" name = "lnbits"
version = "1.5.5rc2" version = "1.5.4"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiosqlite" }, { name = "aiosqlite" },
@@ -1311,7 +1302,6 @@ dependencies = [
{ name = "protobuf" }, { name = "protobuf" },
{ name = "pycryptodomex" }, { name = "pycryptodomex" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pyinstrument" },
{ name = "pyjwt" }, { name = "pyjwt" },
{ name = "pyln-client" }, { name = "pyln-client" },
{ name = "pynostr" }, { name = "pynostr" },
@@ -1339,7 +1329,6 @@ breez = [
{ name = "breez-sdk-liquid" }, { name = "breez-sdk-liquid" },
] ]
liquid = [ liquid = [
{ name = "boltz-client" },
{ name = "wallycore" }, { name = "wallycore" },
] ]
migration = [ migration = [
@@ -1358,6 +1347,7 @@ dev = [
{ name = "openai" }, { name = "openai" },
{ name = "openapi-spec-validator" }, { name = "openapi-spec-validator" },
{ name = "pre-commit" }, { name = "pre-commit" },
{ name = "pyinstrument" },
{ name = "pytest" }, { name = "pytest" },
{ name = "pytest-cov" }, { name = "pytest-cov" },
{ name = "pytest-httpserver" }, { name = "pytest-httpserver" },
@@ -1376,7 +1366,6 @@ requires-dist = [
{ name = "bcrypt", specifier = "~=5.0.0" }, { name = "bcrypt", specifier = "~=5.0.0" },
{ name = "bech32", specifier = "~=1.2.0" }, { name = "bech32", specifier = "~=1.2.0" },
{ name = "bolt11", specifier = "~=2.1.1" }, { name = "bolt11", specifier = "~=2.1.1" },
{ name = "boltz-client", marker = "extra == 'liquid'", specifier = "==0.4.0" },
{ name = "breez-sdk", marker = "extra == 'breez'", specifier = "~=0.8.0" }, { name = "breez-sdk", marker = "extra == 'breez'", specifier = "~=0.8.0" },
{ name = "breez-sdk-liquid", marker = "extra == 'breez'", specifier = "~=0.11.11" }, { name = "breez-sdk-liquid", marker = "extra == 'breez'", specifier = "~=0.11.11" },
{ name = "click", specifier = "~=8.3.1" }, { name = "click", specifier = "~=8.3.1" },
@@ -1399,7 +1388,6 @@ requires-dist = [
{ name = "psycopg2-binary", marker = "extra == 'migration'", specifier = "~=2.9.11" }, { name = "psycopg2-binary", marker = "extra == 'migration'", specifier = "~=2.9.11" },
{ name = "pycryptodomex", specifier = "~=3.23.0" }, { name = "pycryptodomex", specifier = "~=3.23.0" },
{ name = "pydantic", specifier = "~=1.10.26" }, { name = "pydantic", specifier = "~=1.10.26" },
{ name = "pyinstrument", specifier = ">=5.1.2" },
{ name = "pyjwt", specifier = "~=2.12.0" }, { name = "pyjwt", specifier = "~=2.12.0" },
{ name = "pyln-client", specifier = "~=25.12.0" }, { name = "pyln-client", specifier = "~=25.12.0" },
{ name = "pynostr", specifier = "~=0.7.0" }, { name = "pynostr", specifier = "~=0.7.0" },
@@ -1435,6 +1423,7 @@ dev = [
{ name = "openai", specifier = "~=2.14.0" }, { name = "openai", specifier = "~=2.14.0" },
{ name = "openapi-spec-validator", specifier = "~=0.7.2" }, { name = "openapi-spec-validator", specifier = "~=0.7.2" },
{ name = "pre-commit", specifier = "~=4.5.1" }, { name = "pre-commit", specifier = "~=4.5.1" },
{ name = "pyinstrument", specifier = ">=5.1.2" },
{ name = "pytest", specifier = "~=9.0.2" }, { name = "pytest", specifier = "~=9.0.2" },
{ name = "pytest-cov", specifier = "~=7.0.0" }, { name = "pytest-cov", specifier = "~=7.0.0" },
{ name = "pytest-httpserver", specifier = "~=1.1.3" }, { name = "pytest-httpserver", specifier = "~=1.1.3" },
@@ -2256,15 +2245,15 @@ wheels = [
[[package]] [[package]]
name = "python-discovery" name = "python-discovery"
version = "1.3.1" version = "1.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "filelock" }, { name = "filelock" },
{ name = "platformdirs" }, { name = "platformdirs" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/48/60/e88788207d81e46362cfbef0d4aaf4c0f49efc3c12d4c3fa3f542c34ebec/python_discovery-1.3.1.tar.gz", hash = "sha256:62f6db28064c9613e7ca76cb3f00c38c839a07c31c00dfe7ed0986493d2150a6", size = 68011, upload-time = "2026-05-12T20:53:36.336Z" } sdist = { url = "https://files.pythonhosted.org/packages/ae/e0/cc5a8653e9a24f6cf84768f05064aa8ed5a83dcefd5e2a043db14a1c5f44/python_discovery-1.3.0.tar.gz", hash = "sha256:d098f1e86be5d45fe4d14bf1029294aabbd332f4321179dec85e76cddce834b0", size = 63925, upload-time = "2026-05-05T14:38:39.769Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/6f/a05a317a66fee0aad270011461f1a63a453ed12471249f172f7d2e2bc7b4/python_discovery-1.3.1-py3-none-any.whl", hash = "sha256:ed188687ebb3b82c01a17cd5ac62fc94d9f6487a7f1a0f9dfe89753fec91039c", size = 33185, upload-time = "2026-05-12T20:53:34.969Z" }, { url = "https://files.pythonhosted.org/packages/30/d4/24d543ab8b8158b7f5a97113c831205f5c900c92c8762b1e7f44b7ea0405/python_discovery-1.3.0-py3-none-any.whl", hash = "sha256:441d9ced3dfce36e113beb35ca302c71c7ef06f3c0f9c227a0b9bb3bd49b9e9f", size = 33124, upload-time = "2026-05-05T14:38:38.539Z" },
] ]
[[package]] [[package]]
@@ -2278,11 +2267,11 @@ wheels = [
[[package]] [[package]]
name = "python-multipart" name = "python-multipart"
version = "0.0.29" version = "0.0.28"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4e/fe/70bd71a6738b09a0bdf6480ca6436b167469ca4578b2a0efbe390b4b0e70/python_multipart-0.0.29.tar.gz", hash = "sha256:643e93849196645e2dbdd81a0f8829a23123ad7f797a84a364c6fb3563f18904", size = 45678, upload-time = "2026-05-17T17:29:47.654Z" } sdist = { url = "https://files.pythonhosted.org/packages/82/54/a85eb421fbdd5007bc5af39d0f4ed9fa609e0fedbfdc2adcf0b34526870e/python_multipart-0.0.28.tar.gz", hash = "sha256:8550da197eac0f7ab748961fc9509b999fa2662ea25cef857f05249f6893c0f8", size = 45314, upload-time = "2026-05-10T11:05:16.596Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/cb/769cfc37177252872a45a71f3fbdde9d51b471a3f3c14bfe95dde3407386/python_multipart-0.0.29-py3-none-any.whl", hash = "sha256:2ddcc971cef266225f54f552d8fa10bcfbb1f14446caec199060daac59ff2d69", size = 29640, upload-time = "2026-05-17T17:29:45.69Z" }, { url = "https://files.pythonhosted.org/packages/f3/a2/43bbc5860b5034e2af4ef99a0e04d726ff329c43e192ef3abaa8d7ecfce5/python_multipart-0.0.28-py3-none-any.whl", hash = "sha256:10faac07eb966c3f48dc415f9dee46c04cb10d58d30a35677db8027c825ed9b6", size = 29438, upload-time = "2026-05-10T11:05:15.052Z" },
] ]
[[package]] [[package]]
@@ -2377,7 +2366,7 @@ wheels = [
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.34.2" version = "2.34.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi" }, { name = "certifi" },
@@ -2385,9 +2374,9 @@ dependencies = [
{ name = "idna" }, { name = "idna" },
{ name = "urllib3" }, { name = "urllib3" },
] ]
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" } sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" }
wheels = [ 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" }, { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" },
] ]
[[package]] [[package]]
@@ -2682,19 +2671,19 @@ wheels = [
[[package]] [[package]]
name = "tornado" name = "tornado"
version = "6.5.6" version = "6.5.5"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/50/57/6d7303a77ae439d9189108f76c0c4fd89ee5e2cc8387bffb55232565c4ed/tornado-6.5.6.tar.gz", hash = "sha256:9a365179fe8ff6b8766f602c0f67c185d778193e9bdd828b19f0b6ed7764177d", size = 518139, upload-time = "2026-05-27T15:35:54.646Z" } sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/0d/b4f481e18c5a51864e6d12b9a05ecf72919696680b747c958c3fc1f4fbae/tornado-6.5.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:65fcfaafb079435c2c19dc9e07c0f1cf0fa9051759ed0a7d0a3ba7ea7f64919c", size = 447737, upload-time = "2026-05-27T15:35:38.122Z" }, { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" },
{ url = "https://files.pythonhosted.org/packages/9e/9c/5430c39fcab1144d35860f457b15e9c08b4bc7ac86764354204e983d6183/tornado-6.5.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:38bc01b4acacded2de63ae78023548e41ebe6fbed3ec05a796d7ae3ad893887e", size = 445899, upload-time = "2026-05-27T15:35:40.519Z" }, { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" },
{ url = "https://files.pythonhosted.org/packages/8b/79/fa7e14a2f939c807a8d30619b4eb604eab219601b78792516ebe22d40cf9/tornado-6.5.6-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b942e6a137fda31ff54bf8e6e2c8d1c37f1f50583f3ed53fb840b53b9601d104", size = 448964, upload-time = "2026-05-27T15:35:42.106Z" }, { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" },
{ url = "https://files.pythonhosted.org/packages/a7/71/bd67d5f5199f937dafe03a49a37989f60f600ff6fef34c79412a829d97bd/tornado-6.5.6-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8666946e70171b8c3f1fc9b7876fac492e84822c4c7f3746f4e8f8bc9ac92a79", size = 449935, upload-time = "2026-05-27T15:35:43.906Z" }, { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" },
{ url = "https://files.pythonhosted.org/packages/cc/a4/c24388c9cf5b3c3a513b56a158af9f23092c9a2810d789e294310797df21/tornado-6.5.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c34cfab7ad6d104f052f55de06d39bbafc5885cfeb4da688803308dbcfa90b7", size = 449767, upload-time = "2026-05-27T15:35:45.793Z" }, { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" },
{ url = "https://files.pythonhosted.org/packages/a5/eb/6a07ad550c3f7b37244bd0becdf293ec3d3e961783d8b720a97df50de1b2/tornado-6.5.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:385f35e4e22fb52551dfcda4cdc8c30c61c2c001aef5ddad99cdfe116952efd3", size = 449174, upload-time = "2026-05-27T15:35:47.485Z" }, { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" },
{ url = "https://files.pythonhosted.org/packages/bb/84/3469e098dccdb6763130e06aacd786bb4363fca7b590a55c101ddf34ed30/tornado-6.5.6-cp39-abi3-win32.whl", hash = "sha256:db475f1b67b2809b10bb16264829087724ca8d24fe4ed47f7b8675cae453ef86", size = 450230, upload-time = "2026-05-27T15:35:49.322Z" }, { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" },
{ url = "https://files.pythonhosted.org/packages/d2/3c/273a04e0b9dd9016f1685cca0c1c8795a71ac88a34a8c889a0b443483226/tornado-6.5.6-cp39-abi3-win_amd64.whl", hash = "sha256:6739bf1e8eb09230f1280ddbd3236f0309db70f2c551a8dbc40f62babdf82f79", size = 450667, upload-time = "2026-05-27T15:35:51.194Z" }, { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" },
{ url = "https://files.pythonhosted.org/packages/02/98/0cffe22a224f60c5fb1e3aa0b76f9da2e1ca78b0e9545e3d077c68ce60a7/tornado-6.5.6-cp39-abi3-win_arm64.whl", hash = "sha256:2543597b24a695d72338a9a77818362d72387c03ae173f1f169eadc5c91466ac", size = 449690, upload-time = "2026-05-27T15:35:52.902Z" }, { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" },
] ]
[[package]] [[package]]
@@ -2711,26 +2700,26 @@ wheels = [
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.26.2" version = "0.25.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-doc" }, { name = "annotated-doc" },
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "click" },
{ name = "rich" }, { name = "rich" },
{ name = "shellingham" }, { name = "shellingham" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/67/a5/756f2e6bc81a7dd79aa3c625dd01b74cabc4516628cace2caaec09ca6ff2/typer-0.26.2.tar.gz", hash = "sha256:9b4f19e08fcc9427a822d1ef467b1fe76737a2f65c7926bdeba2337d73569b68", size = 198991, upload-time = "2026-05-27T10:41:39.166Z" } sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/a5/6ffd702beda8798b2b82ff70805ed4a66d963557e43a5d1823ab456251a4/typer-0.26.2-py3-none-any.whl", hash = "sha256:39beff72ffbb31978a5b545f677d57edb97c6f980f433b38556deb0af25f094d", size = 123123, upload-time = "2026-05-27T10:41:40.504Z" }, { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" },
] ]
[[package]] [[package]]
name = "types-mock" name = "types-mock"
version = "5.2.0.20260518" version = "5.2.0.20260508"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ec/a4/26595e2a9407752c2e3cd5b17d7884db847e39834a2575cf15b5c3b19a27/types_mock-5.2.0.20260518.tar.gz", hash = "sha256:49af9c18aac4caa90e0e1e8437e2160cd8b3f126053dae6453d65b393590fcf9", size = 11577, upload-time = "2026-05-18T06:02:42.607Z" } sdist = { url = "https://files.pythonhosted.org/packages/a7/54/cefdd866e8b69be4ce93288710a55506bf3f8c5eba8c0b0b8322131bd25d/types_mock-5.2.0.20260508.tar.gz", hash = "sha256:2049474a82a678e1979d7a1273891daccab4c19bbf1b28df5594836d675873d3", size = 11535, upload-time = "2026-05-08T04:49:03.87Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/82/d6/da3bf7cc26ebe587e8c50a505d302f5755840fc24fbbd704b770c43b764b/types_mock-5.2.0.20260518-py3-none-any.whl", hash = "sha256:3c511875b6f37d30c70add3e72265d1c21202b8544751361e3ca94f7a757a03d", size = 10459, upload-time = "2026-05-18T06:02:41.224Z" }, { url = "https://files.pythonhosted.org/packages/86/bf/080b185263af2fc0c25ddf26163f9c59d24caa0edc8c885ba93725fd1ad6/types_mock-5.2.0.20260508-py3-none-any.whl", hash = "sha256:eb90adc8527fba4d800b0d65268f141978789a0398a78ba6bda453cd62de4862", size = 10455, upload-time = "2026-05-08T04:49:03.045Z" },
] ]
[[package]] [[package]]
@@ -2811,7 +2800,7 @@ wheels = [
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "21.3.3" version = "21.3.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "distlib" }, { name = "distlib" },
@@ -2820,9 +2809,9 @@ dependencies = [
{ name = "python-discovery" }, { name = "python-discovery" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/15/ba/1f6e8c957e4932be060dcdc482d339c12e0216351478add3645cdaa53c05/virtualenv-21.3.3.tar.gz", hash = "sha256:f5bda277e553b1c2b3c1a8debfc30496e1288cc93ce6b7b71b3280047e317328", size = 7613784, upload-time = "2026-05-13T18:01:30.19Z" } sdist = { url = "https://files.pythonhosted.org/packages/ec/0d/915c02c94d207b85580eb09bffab54438a709e7288524094fe781da526c2/virtualenv-21.3.1.tar.gz", hash = "sha256:c2305bc1fddeec40699b8370d13f8d431b0701f00ce895061ce493aeded4426b", size = 7613791, upload-time = "2026-05-05T01:34:31.402Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/34/a9dbe051de88a63eb7408ea66630bac38e72f7f6077d4be58737106860d9/virtualenv-21.3.3-py3-none-any.whl", hash = "sha256:7d5987d8369e098e41406efb780a3d4ca79280097293899e351a6407ee153ab3", size = 7594554, upload-time = "2026-05-13T18:01:27.815Z" }, { url = "https://files.pythonhosted.org/packages/b1/4f/f71e641e504111a5a74e3a20bc52d01bd86788b22699dd3fee1c63253cf6/virtualenv-21.3.1-py3-none-any.whl", hash = "sha256:d1a71cf58f2f9228fff23a1f6ec15d39785c6b32e03658d104974247145edd35", size = 7594539, upload-time = "2026-05-05T01:34:28.98Z" },
] ]
[[package]] [[package]]
@@ -2934,107 +2923,110 @@ wheels = [
[[package]] [[package]]
name = "wrapt" name = "wrapt"
version = "2.2.1" version = "2.1.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2d/9f/06263fcd8ad6c405f05a3905fd7a84dd3176eb5ad46e44bccc0cd16348bb/wrapt-2.2.1.tar.gz", hash = "sha256:6744f504375775d7609c82c8d3d94af1c9a6f05586984536905908ba905277b9", size = 127620, upload-time = "2026-05-22T14:49:43.056Z" } sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/8b/84bc1ea68b620fe0e2696a8cff07e82f4b962d952ab14efee8955997bb70/wrapt-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0f68f478004475d97906686e702ddbddeaf717c0b68ad2794384308f2dc713ae", size = 80093, upload-time = "2026-05-22T14:47:27.074Z" }, { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" },
{ url = "https://files.pythonhosted.org/packages/f3/8f/64ec81194a0bc708d9720174c998c8a32116e82b5b32c04e20a7fe01176c/wrapt-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e422b2d647a65d6b080cad5accd09055d3809bdff00c76fba8dca00ca935572a", size = 81183, upload-time = "2026-05-22T14:47:29.062Z" }, { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" },
{ url = "https://files.pythonhosted.org/packages/94/c2/3d186944aae923631d1def58f4c4ff8f0b6309906afc0b6978de3e69b3e0/wrapt-2.2.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:036dfb40128819a751c6f451c6b9c10172c49e4c401aebcdb8ecf2aec1683598", size = 152494, upload-time = "2026-05-22T14:47:30.583Z" }, { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" },
{ url = "https://files.pythonhosted.org/packages/01/d1/6b3d0ea995b867d2862aad5619bd5e17de09a9d64a821f46832dcd272d40/wrapt-2.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09ac16c081bebfd15d8e4dfa5bdc805990bbd52249ecff22530da7a129d6120b", size = 154310, upload-time = "2026-05-22T14:47:32.175Z" }, { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" },
{ url = "https://files.pythonhosted.org/packages/f9/4b/37ecb90a8c3753e580327fb40731a984b754e3df65d2ef932bf359fe4adc/wrapt-2.2.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:07be671fa8875971222b0ba9059ed8b4dc738631122feba17c93aa36b4213e9a", size = 149002, upload-time = "2026-05-22T14:47:34.021Z" }, { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" },
{ url = "https://files.pythonhosted.org/packages/e7/d0/918884d9dfa84d0d135b42a51c00910f5c5447fe7a5e211a8e16ac324dd4/wrapt-2.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93fc2bf40cd7f4a0256010dce073d44eeb4a351b9bca94d0477ce2b6e62532b3", size = 153185, upload-time = "2026-05-22T14:47:35.722Z" }, { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" },
{ url = "https://files.pythonhosted.org/packages/4c/00/382299d8ced610b29b59b099a89eda821e8c489aa152b7183748ac83f32a/wrapt-2.2.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ba519b2d765df9871a25879e6f7fa78948ea59a2a31f9c1a257e34b651994afc", size = 148040, upload-time = "2026-05-22T14:47:37.052Z" }, { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" },
{ url = "https://files.pythonhosted.org/packages/6c/46/62a79b79e35bbebb1207ca5d15b81192f37f20cc5659cf4e3ce955b7fcc8/wrapt-2.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9011395be8db1827d106c6449b4bb6dd17e331ff6ec521f227e4588f1c78e46f", size = 151773, upload-time = "2026-05-22T14:47:38.713Z" }, { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" },
{ url = "https://files.pythonhosted.org/packages/a1/db/95c152151d206d4b430516c89725306e92484072f38e65492afde63f6d19/wrapt-2.2.1-cp310-cp310-win32.whl", hash = "sha256:a8f7176b83664af44567e9cc06e0d3827823fcc1a5e52307ebb8ac3aa95860b9", size = 77393, upload-time = "2026-05-22T14:47:40.061Z" }, { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" },
{ url = "https://files.pythonhosted.org/packages/13/d3/882d50452c6fbd13f24fe5d2644b97cdad2565a7e1522cbb6312de8a52cf/wrapt-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:d7f513d3185e6fec82d0c3518f2e6365d8b4e49f5f45f29640d5162d56a23b54", size = 80350, upload-time = "2026-05-22T14:47:41.194Z" }, { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" },
{ url = "https://files.pythonhosted.org/packages/58/0f/148376523b4e370692286a9ba14d5715cf3c5b86da3bd3630926367b6b73/wrapt-2.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:44255c84bc57554fed822e83e70036b51afa9edb56fc7ca56c54410ece7898c9", size = 79149, upload-time = "2026-05-22T14:47:42.835Z" }, { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" },
{ url = "https://files.pythonhosted.org/packages/5f/ac/4370bde262c0e633e6c4f0e56d55095710024cf9a5cecc20c59a10de483c/wrapt-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd57607acc85678925940bd5df0385ff8332083a32fa8d7a43f8767f4997263c", size = 80321, upload-time = "2026-05-22T14:47:43.996Z" }, { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" },
{ url = "https://files.pythonhosted.org/packages/eb/79/b8ff3a61e71babf58a8cf4c0d63358e8bad383e15bf7f35e62d2f6b6e4a4/wrapt-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ae574d65c9fa8e86f64f6a7c2668f9fcd507b183e0e577619f504b883cb0a6c", size = 81216, upload-time = "2026-05-22T14:47:45.243Z" }, { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" },
{ url = "https://files.pythonhosted.org/packages/6e/fd/c0cac1f77c9c4f6fe58a920ca632ce379bb8be928720e11e8d73de28a5e9/wrapt-2.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a04c28c10ba7fd12842b109d2edb0678872a2fe65277ca4ff06a0d61edee245", size = 159208, upload-time = "2026-05-22T14:47:47.176Z" }, { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" },
{ url = "https://files.pythonhosted.org/packages/d9/4f/744132a7b2fbefa6b81118ec5942eca5fc2e9a129f9055a0c5e46885a549/wrapt-2.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2f02472a1cbbf3884b365714a810b5947134a95ad6952b554cb8cce9d492b0", size = 160322, upload-time = "2026-05-22T14:47:49.04Z" }, { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" },
{ url = "https://files.pythonhosted.org/packages/d6/95/b7cd9a22a06cf93e6482904ee6afc956248983553593fd1009296d1b3b31/wrapt-2.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac2745950b2bff80219c15ebf2fa9d8427eba7e249739f97e55c9d169e47e9e1", size = 153243, upload-time = "2026-05-22T14:47:50.386Z" }, { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" },
{ url = "https://files.pythonhosted.org/packages/4c/4a/eb79423192015f46f0db2872e7e04a3dde8d359b83411e8959e7c9287eaa/wrapt-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67a97e5b6c457f0cd3cfc19ebb2d84463e60c3ece754cc831e4281a3ca29bb18", size = 159231, upload-time = "2026-05-22T14:47:51.753Z" }, { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" },
{ url = "https://files.pythonhosted.org/packages/ec/dc/435015b58ce33c6fc4104158fa91ddb0e809ab03a5751fb7465d1d461456/wrapt-2.2.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c803a3d331796255af51ba2c79ed0ac8275865b516c09e61f248d1e7aff31ce9", size = 152351, upload-time = "2026-05-22T14:47:53.214Z" }, { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" },
{ url = "https://files.pythonhosted.org/packages/77/ac/5d203f98df8fd136b95c5227139aea02d34505e18baf812d0c005df61963/wrapt-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9b984d1eb252145d6302c1dbd5e87fc6d404d45531447c84eadec04bf1fcb027", size = 158347, upload-time = "2026-05-22T14:47:54.982Z" }, { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" },
{ url = "https://files.pythonhosted.org/packages/52/2f/a92427dbdc74e54c1674abbed27e61b2cb5e7a94441b8c1270c70671d928/wrapt-2.2.1-cp311-cp311-win32.whl", hash = "sha256:8a983a603a18c8708f024f7f6991b2e66159219abbf894634c5056243c55f3cd", size = 77562, upload-time = "2026-05-22T14:47:56.275Z" }, { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" },
{ url = "https://files.pythonhosted.org/packages/c8/56/987b9c13b3e1c1a3c6de71284076f996b79caec90e75a87c044a40c23db9/wrapt-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:9c210a6994b21aa9b29e81c8d11560e8fdab54c117e9cff37870d0a27bde1343", size = 80616, upload-time = "2026-05-22T14:47:57.854Z" }, { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" },
{ url = "https://files.pythonhosted.org/packages/7e/25/d01f560888d99d94a959c85533de349ce68d71ace3f2591d6ea8f632cfed/wrapt-2.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:401229e9d63ca09f9b8891ecf83798d26c11bbb445d11ed9f1836b6d4585b38a", size = 79025, upload-time = "2026-05-22T14:47:59.089Z" }, { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" },
{ url = "https://files.pythonhosted.org/packages/89/0c/bfae7b9401583b6d05938cd16dedc43857d96da2f8a3d50d78cc515bf6ff/wrapt-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ffad790d9d11d8ecf9f17c4bb671a5b4089e4d8b575c46c5129597f41f836b0", size = 81021, upload-time = "2026-05-22T14:48:00.313Z" }, { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" },
{ url = "https://files.pythonhosted.org/packages/26/58/80f6a6599f933f4caecc1cb3ee88a04faf81e8b9bddbd6109c688dd63e0f/wrapt-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:628f5220c7a904d5fc78f7075c8d7871433eb6d035c94728a22fdf85f193d2a8", size = 81692, upload-time = "2026-05-22T14:48:01.49Z" }, { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" },
{ url = "https://files.pythonhosted.org/packages/17/93/fb357cc7847c58a8ae790be718903afa81a28d23e642c843dc4129e8a0b2/wrapt-2.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:61acce4257a9883669703c525447c5b4c392edf0f987ae77ec32668440158f0e", size = 169364, upload-time = "2026-05-22T14:48:02.791Z" }, { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" },
{ url = "https://files.pythonhosted.org/packages/aa/0b/76b601ee309a8bd556af0eecb184394c20b3c49aa9c8e085aa1ffacc2568/wrapt-2.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727ab4244622cd6ad2390f322642090c877d2e83a608d2653a7643ae5368d926", size = 171079, upload-time = "2026-05-22T14:48:04.22Z" }, { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" },
{ url = "https://files.pythonhosted.org/packages/cd/87/ee3f32d5658e3e26d3e0e457922b47a36dd3bfbdfee7f97bb3e802344a66/wrapt-2.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03df9ebed4c73ab93fa8c07e3d41d818dfca1852b15731a3de59457b27814624", size = 160205, upload-time = "2026-05-22T14:48:05.553Z" }, { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" },
{ url = "https://files.pythonhosted.org/packages/b1/d0/ae2fd64277a67f5d7bffcf2d05eea1e476263fb2a072baf0b0129ab85984/wrapt-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0d9ff006f420b2ec8296aa56ade43ea7da3e997e85769f0aafc5e0661aacb710", size = 168922, upload-time = "2026-05-22T14:48:07.132Z" }, { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" },
{ url = "https://files.pythonhosted.org/packages/b1/f3/2d541a060c5bbafb9400bca4917e4d78bfd1f239f404782c86831a8f6b29/wrapt-2.2.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:844c858fc3bb7eacc0ba8efa904935d16aac6a4470948ad1e7e55c9f5a2a665f", size = 158388, upload-time = "2026-05-22T14:48:08.629Z" }, { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" },
{ url = "https://files.pythonhosted.org/packages/1d/68/8d92c8800c57e93cb116ae9e9d6cbafc34fade5ee9f9107b6f203fb4dc35/wrapt-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87bacdaf225117a342a20d9c03438d701c02112f6e3f351ce9b7f32354f14797", size = 167682, upload-time = "2026-05-22T14:48:10.042Z" }, { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" },
{ url = "https://files.pythonhosted.org/packages/30/72/83ea3790ea352439442349388e29ff07b76e0686265f9088bbb505d1608d/wrapt-2.2.1-cp312-cp312-win32.whl", hash = "sha256:2f8c90c8afde51969487be4e1343ae049b268854877d415c2510baf833775052", size = 77857, upload-time = "2026-05-22T14:48:11.782Z" }, { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" },
{ url = "https://files.pythonhosted.org/packages/ef/cb/99450668dd3502d62a54a1c8aa56e44f34cb8c1261b381cfe2e7926c3b75/wrapt-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ce32763ac31ce94fe9aada947e479b1975012bff166da409b4b9e4e376cf7e5", size = 80825, upload-time = "2026-05-22T14:48:13.046Z" }, { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" },
{ url = "https://files.pythonhosted.org/packages/e6/3a/87512881be64e743f9ee4c66f4cbe8e884974bef2a5989af71f999653ac7/wrapt-2.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d1b4d0e0c2119587a31f5c029abd547e0c81d93b89d394566fe1588659eb579", size = 79087, upload-time = "2026-05-22T14:48:14.323Z" }, { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" },
{ url = "https://files.pythonhosted.org/packages/53/46/29ac9daf11a86c22a8c38cd9236c62928ccae83f7ceb06bd3b0467cf9d05/wrapt-2.2.1-py3-none-any.whl", hash = "sha256:3aafea2975caef8ca49400640dde02cc7426e798f24870ed01f490bc3cffd32f", size = 61000, upload-time = "2026-05-22T14:49:41.593Z" }, { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" },
] ]
[[package]] [[package]]
name = "yarl" name = "yarl"
version = "1.24.2" version = "1.23.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "idna" }, { name = "idna" },
{ name = "multidict" }, { name = "multidict" },
{ name = "propcache" }, { name = "propcache" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/3f/df/f1c7a3de0831cd83194f1a85c5bb431b13f81e6b45079314c86d1c4ef3f2/yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12", size = 129057, upload-time = "2026-05-19T21:27:47.564Z" }, { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" },
{ url = "https://files.pythonhosted.org/packages/48/41/7daafb32dd7562bf45b1ce56562e7e1a9146f6479b6456873eb8a3413c40/yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0", size = 91545, upload-time = "2026-05-19T21:27:50.089Z" }, { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" },
{ url = "https://files.pythonhosted.org/packages/a8/8f/7b3ec212f1ea0683f55f978e3246bc313c38818664edfc97a9f349a4901e/yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75", size = 91380, upload-time = "2026-05-19T21:27:51.953Z" }, { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" },
{ url = "https://files.pythonhosted.org/packages/8a/1b/8bafab7db23b0567ae9db749099b329d91e3b82bc6028b2050ba583e116c/yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727", size = 105957, upload-time = "2026-05-19T21:27:53.98Z" }, { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" },
{ url = "https://files.pythonhosted.org/packages/7f/77/21030c2f8d21d21559719beafc772ada2014be933418ed1eaed9cc800e42/yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413", size = 97242, upload-time = "2026-05-19T21:27:55.981Z" }, { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" },
{ url = "https://files.pythonhosted.org/packages/50/d8/f9ea63d1b6aa910a866e089d871fff6cbd49caab29b86b35221a62dfa0d5/yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9", size = 114719, upload-time = "2026-05-19T21:27:58.037Z" }, { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" },
{ url = "https://files.pythonhosted.org/packages/e9/a3/04e0ee98ac58a249ea7ed75223f5f901ba81a834f0b4921b58e5cec11757/yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2", size = 112140, upload-time = "2026-05-19T21:27:59.618Z" }, { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" },
{ url = "https://files.pythonhosted.org/packages/02/ad/0b9cc9f38a7324a7eb1d80f834eaa5283d17e9271bbda3186e598dddaeac/yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90", size = 106721, upload-time = "2026-05-19T21:28:02.586Z" }, { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" },
{ url = "https://files.pythonhosted.org/packages/65/e7/a52478ebfc66ec989e085c6ae038b9f1bfa4190baa193b133b669c709e2f/yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643", size = 106478, upload-time = "2026-05-19T21:28:04.523Z" }, { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" },
{ url = "https://files.pythonhosted.org/packages/04/d8/5508530fea8472542de00013ae280765fc938ee196fc4030c43a498afb36/yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac", size = 105423, upload-time = "2026-05-19T21:28:06.515Z" }, { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" },
{ url = "https://files.pythonhosted.org/packages/84/f1/ece28505e9628e8b756e11bb4f28864a17cc33b6b44db4d2aaf0622bf630/yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f", size = 99878, upload-time = "2026-05-19T21:28:08.637Z" }, { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" },
{ url = "https://files.pythonhosted.org/packages/3f/52/fb5d34529b46dd84013afcfb30b8d2bc2832ed03d412736f577d604fa393/yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36", size = 114025, upload-time = "2026-05-19T21:28:10.64Z" }, { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" },
{ url = "https://files.pythonhosted.org/packages/43/f0/ff9d31aaab024f7a251c0ed308a98ae29bf9f7dc344e78f28b1322431ca2/yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a", size = 105613, upload-time = "2026-05-19T21:28:12.784Z" }, { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" },
{ url = "https://files.pythonhosted.org/packages/31/7d/3296fb3f3ecd52bf9ae6c16b0895c1cda7e9170a2083861552b683f70264/yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53", size = 111665, upload-time = "2026-05-19T21:28:14.393Z" }, { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" },
{ url = "https://files.pythonhosted.org/packages/1a/74/77aa6ddaca4fbf42e45e675a465c43956dd40702281049975a2aa04eae59/yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342", size = 106914, upload-time = "2026-05-19T21:28:15.893Z" }, { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" },
{ url = "https://files.pythonhosted.org/packages/d8/02/7611f22cd1d4ed7373eb7f9ee21fde1046edba2e7c0e514880d760352f48/yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4", size = 92658, upload-time = "2026-05-19T21:28:17.471Z" }, { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" },
{ url = "https://files.pythonhosted.org/packages/91/00/671d0add79938127292839ae44506ce2f7fe8909c72d5a931864f128fd0b/yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39", size = 87887, upload-time = "2026-05-19T21:28:19.021Z" }, { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" },
{ url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971, upload-time = "2026-05-19T21:28:20.543Z" }, { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" },
{ url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507, upload-time = "2026-05-19T21:28:22.556Z" }, { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" },
{ url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343, upload-time = "2026-05-19T21:28:24.092Z" }, { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" },
{ url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704, upload-time = "2026-05-19T21:28:25.872Z" }, { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" },
{ url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281, upload-time = "2026-05-19T21:28:27.352Z" }, { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" },
{ url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020, upload-time = "2026-05-19T21:28:29.458Z" }, { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" },
{ url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450, upload-time = "2026-05-19T21:28:31.262Z" }, { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" },
{ url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384, upload-time = "2026-05-19T21:28:32.856Z" }, { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" },
{ url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153, upload-time = "2026-05-19T21:28:35.222Z" }, { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" },
{ url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322, upload-time = "2026-05-19T21:28:36.642Z" }, { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" },
{ url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057, upload-time = "2026-05-19T21:28:38.386Z" }, { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" },
{ url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502, upload-time = "2026-05-19T21:28:40.091Z" }, { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" },
{ url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253, upload-time = "2026-05-19T21:28:41.987Z" }, { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" },
{ url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345, upload-time = "2026-05-19T21:28:43.93Z" }, { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" },
{ url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558, upload-time = "2026-05-19T21:28:45.806Z" }, { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" },
{ url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808, upload-time = "2026-05-19T21:28:48.465Z" }, { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" },
{ url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610, upload-time = "2026-05-19T21:28:50.07Z" }, { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" },
{ url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" },
{ url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" },
{ url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" },
{ url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" },
{ url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" },
{ url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" },
{ url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" },
{ url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" },
{ url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" },
{ url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" },
{ url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" },
{ url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" },
{ url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" },
{ url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" },
{ url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" },
{ url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" },
{ url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" },
{ url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" },
{ url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" },
{ url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" },
{ url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" },
] ]