37 Commits

Author SHA1 Message Date
Thomas Peetz 7555cfd51d Update Vaadin and Spring Boot version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
2026-08-13 14:04:30 +02:00
Thomas Peetz ea038ec70c refactor package layout
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 7s
2026-08-12 19:05:06 +02:00
Thomas Peetz ecfa0eb6d8 remove subprojects integration and kontor-data
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-08-12 15:19:04 +02:00
Thomas Peetz fe63a80b23 update vaadin and lombok versionsand disable asciidoctor
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-08-12 13:28:41 +02:00
tpeetz 567e254840 add kontor-data to extract data model
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 10s
2026-08-10 22:49:46 +02:00
Thomas Peetz a4975928ae add integration subproject for Camel routes
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 52s
2026-08-10 15:23:32 +02:00
Thomas Peetz 1dcf342736 add queues and processing
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-08-05 15:46:39 +02:00
tpeetz 6abbe2e585 Add reading queue media.link.add
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-08-04 23:22:26 +02:00
tpeetz 8eecd47399 setup basic project with Python Robyn
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2026-08-02 17:39:41 +02:00
tpeetz 9d01925097 add kontor-robyn
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2026-07-31 22:46:46 +02:00
Thomas Peetz bd721a8e64 add msgspec to Python projects
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 18:28:23 +02:00
Thomas Peetz c4932a4665 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 04:07:07 +02:00
Thomas Peetz def3f7ea11 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 04:06:35 +02:00
Thomas Peetz bef7b0bd59 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-31 04:06:02 +02:00
Thomas Peetz 5655bc5322 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 04:05:23 +02:00
Thomas Peetz 375438a8fa use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 2s
2026-07-31 04:02:25 +02:00
Thomas Peetz 90b1e8def3 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 04:01:54 +02:00
Thomas Peetz 816165579c use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2026-07-31 04:00:04 +02:00
Thomas Peetz fbcdbb3e1a use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 7s
2026-07-31 03:59:26 +02:00
Thomas Peetz e46ab25920 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2026-07-31 03:57:45 +02:00
Thomas Peetz 1fa64bf5ce use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-31 02:40:12 +02:00
Thomas Peetz fdb9da358a use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-31 00:59:26 +02:00
Thomas Peetz 235f64aa08 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-31 00:57:12 +02:00
Thomas Peetz 9f17823922 use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-30 22:37:35 +02:00
Thomas Peetz 0c6b26b79e use msgspec to parse message
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-30 22:35:25 +02:00
Thomas Peetz c7a322f29d add msgspec to Python projects
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 9s
2026-07-30 17:37:17 +02:00
tpeetz 32dcd43fe5 add message sending with stomp protocol
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-26 22:08:49 +02:00
tpeetz 5edb929b0f fix generating Container image build
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 11s
2026-07-26 19:59:56 +02:00
tpeetz f0cf98d36e remove asciidoctor from kontor-spring
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-23 05:12:03 +02:00
tpeetz 19e7eba250 add check for short filenames
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-22 23:20:51 +02:00
tpeetz 81d6018e0f add Nexus config
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-22 22:54:36 +02:00
tpeetz c685e9aca5 fix SNAPSHOT Version
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-22 17:09:36 +00:00
tpeetz d39c14cd9a change creation of MediaFile
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-07-22 19:00:39 +02:00
tpeetz 2ce6bbc083 fix messageobject
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-07-21 20:51:20 +00:00
tpeetz 3a351ac5b7 fix json conversion
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 8s
2026-07-21 18:55:42 +00:00
tpeetz 31c01c3033 adding dry-run option
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 9s
2026-07-11 17:38:24 +02:00
tpeetz 0d5ee0dd63 implement REST API for adding MediaFileActors
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 11s
2026-06-14 01:58:57 +02:00
452 changed files with 142636 additions and 7375 deletions
+5
View File
@@ -6,3 +6,8 @@ node_modules/
.editorconfig
db-password.txt
couchdb-password.txt
kontor-robyn/kontor.db
kontor-robyn.bak
kontor-robyn.bak2
kontor-data/.gradle
kontor-data/build/
+1
View File
@@ -29,4 +29,5 @@ dependencies = [
"asyncpg>=0.30.0",
"bcrypt>=4.3.0",
"fastapi-jwt-auth>=0.5.0",
"msgspec[toml,yaml]>=0.21.1",
]
@@ -1,9 +1,10 @@
from typing import List
from fastapi import APIRouter, status, HTTPException
from src.core.log_conf import logger
from src.db.models.media import MediaActorFile
from src.db.repository.media.actorfile import delete_mediaactorfile
from src.db.repository.media.actorfile import delete_mediaactorfile, import_mediaactorfile
from src.db.session import SessionDep
from src.schema.media.actorfile import MediaActorFileResponse, actorfile_to_response
from src.schema.media.actorfile import MediaActorFileModel, MediaActorFileResponse, actorfile_to_response
router = APIRouter()
@@ -30,3 +31,13 @@ def delete_actorfile(actorfile_id: str, db: SessionDep):
if not media_actorfile:
raise HTTPException(status_code=404, detail="MediaActor could not be found")
delete_mediaactorfile(db, media_actorfile.id)
@router.post("/actorfiles", status_code=status.HTTP_201_CREATED)
def add_actorfile(new_actorfile: MediaActorFileModel, db: SessionDep) -> MediaActorFileResponse:
logger.info("add actorfile %s - %s", new_actorfile.media_actor_id, new_actorfile.media_file_id)
try:
mediaActorFile: MediaActorFile = import_mediaactorfile(db, new_actorfile)
except Exception as exception:
raise HTTPException(status_code=409, detail=f"Link duplicate: {exception}")
response = actorfile_to_response(mediaActorFile)
return response
@@ -46,4 +46,19 @@ def import_mediaactorfile(
"""
logger.info("import MediaActorFile with %s", new_actorfile)
media_actor_file: MediaActorFile = MediaActorFile()
media_actor_file.id = new_actorfile.id
if new_actorfile.created_date:
media_actor_file.created_date = new_actorfile.created_date
else:
media_actor_file.created_date = datetime.now()
if new_actorfile.last_modified_date:
media_actor_file.last_modified_date = new_actorfile.last_modified_date
else:
media_actor_file.last_modified_date = datetime.now()
media_actor_file.version = new_actorfile.version
media_actor_file.media_actor_id = new_actorfile.media_actor_id
media_actor_file.media_file_id = new_actorfile.media_file_id
db.add(media_actor_file)
db.commit()
db.refresh(media_actor_file)
return media_actor_file
+454 -405
View File
File diff suppressed because it is too large Load Diff
+34 -6
View File
@@ -1,11 +1,6 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This is a general purpose Gradle build.
* Learn more about Gradle by exploring our Samples at https://docs.gradle.org/9.4.1/samples
*/
plugins {
id("base")
id("maven-publish")
id("de.infolektuell.typst") version "0.8.0"
}
@@ -16,6 +11,39 @@ typst.sourceSets {
val main by registering {
// The files to compile (without .typ extension) in src/main/typst
documents = listOf("kontor") // src/main/typst/document.typst
inputs.put("version", version.toString())
}
}
tasks.withType<AbstractPublishToMaven>().configureEach {
dependsOn(tasks.named("compileTypst"))
}
publishing {
publications {
create<MavenPublication>("maven") {
artifactId = "kontor"
artifact(file("build/typst/main/pdf/kontor.pdf")) {
classifier = "docs"
extension = "pdf"
}
}
}
repositories {
maven {
// Determine URL based on version
url = uri(
if (version.toString().endsWith("SNAPSHOT")) {
"https://nexus.thpeetz.de/repository/maven-snapshots/"
} else {
"https://nexus.thpeetz.de/repository/maven-releases/"
}
)
// Credentials
credentials {
username = project.findProperty("nexusUsername") as String? ?: System.getenv("NEXUS_USERNAME")
password = project.findProperty("nexusPassword") as String? ?: System.getenv("NEXUS_PASSWORD")
}
}
}
}
+3 -1
View File
@@ -1,5 +1,7 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
description='Kontor Documentation'
version=0.3.0-SNAPSHOT
group=de.thpeetz
org.gradle.configuration-cache=true
+5 -6
View File
@@ -1,5 +1,6 @@
#let version = sys.inputs.at("version", default: "0.0.1")
//#set page("a4")
#import "@preview/basic-report:0.4.0": *
#import "@preview/basic-report:0.5.0": *
#import "@preview/in-dexter:0.7.2": *
#import "@preview/pintorita:0.1.4"
@@ -8,14 +9,12 @@
#show: it => basic-report(
doc-category: "Entwicklungs- und Projekthandbuch",
doc-title: "Projekt kontor",
doc-title: "Projekt kontor\nVersion " + version,
author: "Thomas Peetz",
//affiliation: "MouseTec, Entenhausen",
//logo: image("assets/aerospace-engineering.png", width: 2cm),
// <a href="https://www.flaticon.com/free-icons/aerospace" title="aerospace icons">Aerospace icons created by gravisio - Flaticon</a>
language: "de",
compact-mode: false,
it
show-outline: true,
it,
)
= Allgemeines
+1
View File
@@ -0,0 +1 @@
3.13
View File
+22
View File
@@ -0,0 +1,22 @@
[project]
name = "kontor-robyn"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
{ name = "Thomas Peetz", email = "thomas.peetz@thpeetz.de" }
]
requires-python = ">=3.13"
dependencies = [
"asyncpg>=0.31.0",
"msgspec[toml,yaml]>=0.21.1",
"robyn[all]>=0.88.0",
"sqlalchemy>=2.0.51",
]
[project.scripts]
kontor = "kontor_robyn:main"
[build-system]
requires = ["uv_build>=0.12.1,<0.13.0"]
build-backend = "uv_build"
+79
View File
@@ -0,0 +1,79 @@
"""
Main entypoint for kontor-robyn
"""
from contextvars import ContextVar
import time
from robyn import Request, Response, Robyn
from kontor_robyn.apis.version1 import api_router
from kontor_robyn.db.models import Base, engine
from .core.log_conf import logger
_request_start: ContextVar[float] = ContextVar("request_start")
def create_tables() -> None:
"""
Create table with SQLAlchemy
"""
logger.info("create tables")
Base.metadata.create_all(bind=engine)
logger.info("tables created")
def add_logging(app: Robyn) -> None:
"""
set logging for every request
"""
def logging_before_handler(request: Request):
_request_start.set(time.perf_counter())
return request
def log_request(request: Request, response: Response):
"""
log used time and return code
"""
start = _request_start.get(None)
duration_ms = (time.perf_counter() - start) * 1000 if start is not None else 0.0
logger.info(
"%s %s -> %s (%.2fms)",
request.method,
request.url.path,
response.status_code,
duration_ms,
)
return response
logging_before_handler = app.before_request()(logging_before_handler) # type: ignore # noqa: F823
log_request = app.after_request()(log_request) # type: ignore # noqa: F823
def include_router(app: Robyn):
"""
Add routes
"""
app.include_router(api_router)
def start_application() -> None:
"""
Start Robyn framework and configure routes
"""
app = Robyn(__file__)
add_logging(app)
include_router(app)
create_tables()
app.start(port=8280)
def main() -> None:
"""
Start Framework Robyn
"""
logger.info("starting kontor-robyn")
start_application()
logger.info("kontor-robyn exited")
@@ -0,0 +1,16 @@
"""
Provide subrouter for api/v1
"""
from robyn import SubRouter
api_router = SubRouter(prefix="/api/v1")
@api_router.get("/health")
def health():
"""
Return health info
"""
return {"status": "ok"}
@@ -0,0 +1,43 @@
"""
Define logging configuration for kontor-robyn
"""
import logging
import logging.config
from typing import Any
LOGGING_CONFIG: dict[str, Any] = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"fmt": "%(asctime)s - %(name)s - %(levelprefix)s %(message)s",
},
"access": {
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
},
"access_file": {
"fmt": '%(asctime)s - %(name)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s', # noqa: E501
"use_colors": False,
},
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
"error": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
},
},
"loggers": {
"root": {"handlers": ["default"], "level": "INFO", "propagate": False},
"kontor": {"handlers": ["default"], "level": "INFO", "propagate": False},
},
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("kontor")
@@ -0,0 +1,46 @@
"""
Definition of models
"""
from typing import Optional
import uuid
from datetime import datetime
from sqlalchemy import create_engine, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
DATABASE_URL = "sqlite:///./kontor.db"
engine = create_engine(DATABASE_URL)
SESSION_LOCAL = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class Base(DeclarativeBase):
"""
Base class for all Models
"""
class BaseMixin:
"""
Base mixin to provide standard fields id, version, created_date, last_modified_date
"""
id: Mapped[str] = mapped_column(primary_key=True, default=str(uuid.uuid4()))
created_date: Mapped[datetime] = mapped_column(default=func.now())
last_modified_date: Mapped[datetime] = mapped_column(default=func.now())
version: Mapped[int] = mapped_column(default=0)
class BaseVideoMixin:
"""
Base mixin to provide additional fields for video links.
"""
cloud_link: Mapped[Optional[str]]
file_name: Mapped[Optional[str]]
path: Mapped[str]
review: Mapped[bool]
title: Mapped[str]
url: Mapped[str]
should_download: Mapped[bool]
@@ -0,0 +1,102 @@
from typing import List, Optional
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, relationship, mapped_column
from kontor_robyn.db.models import Base, BaseMixin, BaseVideoMixin
class MediaFile(Base, BaseMixin, BaseVideoMixin):
"""
MediaFile represents video link.
"""
__tablename__ = "media_file"
media_actor_files: Mapped[List["MediaActorFile"]] = relationship(
back_populates="media_file"
)
def __repr__(self):
return f"MediaFile({self.id} {self.title} {self.title})"
def __str__(self):
return f"{self.title}({self.id})"
def update_title(self):
"""
Update title from url.
"""
class MediaActor(Base, BaseMixin):
"""
MediaActor represents actor for MediaFile
"""
__tablename__ = "media_actor"
name: Mapped[str]
url: Mapped[Optional[str]] = mapped_column(unique=True)
media_actor_files = relationship("MediaActorFile")
def __repr__(self) -> str:
return f"MediaActor({self.id} {self.name} {self.url})"
def __str__(self) -> str:
return f"{self.url}({self.id})"
class MediaActorFile(Base, BaseMixin):
"""
MediaActorFile defines the connection between MediaFile and MediaActor
"""
__tablename__ = "media_actor_file"
media_actor_id: Mapped[str] = mapped_column(
ForeignKey("media_actor.id"), nullable=False
)
media_actor: Mapped[MediaActor] = relationship(back_populates="media_actor_files")
media_file_id: Mapped[str] = mapped_column(
ForeignKey("media_file.id"), nullable=True
)
media_file: Mapped[MediaFile] = relationship(back_populates="media_actor_files")
def __repr__(self):
return f"MediaActorFile({self.id} {self.media_actor_id} {self.media_file_id})"
def __str__(self) -> str:
return f"{self.id} {self.media_actor_id} {self.media_file_id}"
class MediaArticle(Base, BaseMixin):
"""
MediaArticle represents a link to an article
"""
__tablename__ = "media_article"
review: Mapped[bool]
title: Mapped[str]
url: Mapped[str] = mapped_column(unique=True)
class MediaVideo(Base, BaseMixin):
"""
MediaFile represents video link.
"""
__tablename__ = "media_video"
cloud_link: Mapped[str]
file_name: Mapped[str]
path: Mapped[str]
review: Mapped[bool]
title: Mapped[str]
url: Mapped[str] = mapped_column(unique=True)
should_download: Mapped[bool]
def __repr__(self):
return f"MediaFile({self.id} {self.title} {self.url})"
def __str__(self):
if self.title is None:
return f"{self.url}({self.id})"
else:
return f"{self.title}({self.id})"
+595
View File
@@ -0,0 +1,595 @@
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "annotated-types"
version = "0.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" },
]
[[package]]
name = "asyncpg"
version = "0.31.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fe/cc/d18065ce2380d80b1bcce927c24a2642efd38918e33fd724bc4bca904877/asyncpg-0.31.0.tar.gz", hash = "sha256:c989386c83940bfbd787180f2b1519415e2d3d6277a70d9d0f0145ac73500735", size = 993667, upload-time = "2025-11-24T23:27:00.812Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/11/97b5c2af72a5d0b9bc3fa30cd4b9ce22284a9a943a150fdc768763caf035/asyncpg-0.31.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c204fab1b91e08b0f47e90a75d1b3c62174dab21f670ad6c5d0f243a228f015b", size = 661111, upload-time = "2025-11-24T23:26:04.467Z" },
{ url = "https://files.pythonhosted.org/packages/1b/71/157d611c791a5e2d0423f09f027bd499935f0906e0c2a416ce712ba51ef3/asyncpg-0.31.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:54a64f91839ba59008eccf7aad2e93d6e3de688d796f35803235ea1c4898ae1e", size = 636928, upload-time = "2025-11-24T23:26:05.944Z" },
{ url = "https://files.pythonhosted.org/packages/2e/fc/9e3486fb2bbe69d4a867c0b76d68542650a7ff1574ca40e84c3111bb0c6e/asyncpg-0.31.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0e0822b1038dc7253b337b0f3f676cadc4ac31b126c5d42691c39691962e403", size = 3424067, upload-time = "2025-11-24T23:26:07.957Z" },
{ url = "https://files.pythonhosted.org/packages/12/c6/8c9d076f73f07f995013c791e018a1cd5f31823c2a3187fc8581706aa00f/asyncpg-0.31.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bef056aa502ee34204c161c72ca1f3c274917596877f825968368b2c33f585f4", size = 3518156, upload-time = "2025-11-24T23:26:09.591Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3b/60683a0baf50fbc546499cfb53132cb6835b92b529a05f6a81471ab60d0c/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0bfbcc5b7ffcd9b75ab1558f00db2ae07db9c80637ad1b2469c43df79d7a5ae2", size = 3319636, upload-time = "2025-11-24T23:26:11.168Z" },
{ url = "https://files.pythonhosted.org/packages/50/dc/8487df0f69bd398a61e1792b3cba0e47477f214eff085ba0efa7eac9ce87/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22bc525ebbdc24d1261ecbf6f504998244d4e3be1721784b5f64664d61fbe602", size = 3472079, upload-time = "2025-11-24T23:26:13.164Z" },
{ url = "https://files.pythonhosted.org/packages/13/a1/c5bbeeb8531c05c89135cb8b28575ac2fac618bcb60119ee9696c3faf71c/asyncpg-0.31.0-cp313-cp313-win32.whl", hash = "sha256:f890de5e1e4f7e14023619399a471ce4b71f5418cd67a51853b9910fdfa73696", size = 527606, upload-time = "2025-11-24T23:26:14.78Z" },
{ url = "https://files.pythonhosted.org/packages/91/66/b25ccb84a246b470eb943b0107c07edcae51804912b824054b3413995a10/asyncpg-0.31.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc5f2fa9916f292e5c5c8b2ac2813763bcd7f58e130055b4ad8a0531314201ab", size = 596569, upload-time = "2025-11-24T23:26:16.189Z" },
{ url = "https://files.pythonhosted.org/packages/3c/36/e9450d62e84a13aea6580c83a47a437f26c7ca6fa0f0fd40b6670793ea30/asyncpg-0.31.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f6b56b91bb0ffc328c4e3ed113136cddd9deefdf5f79ab448598b9772831df44", size = 660867, upload-time = "2025-11-24T23:26:17.631Z" },
{ url = "https://files.pythonhosted.org/packages/82/4b/1d0a2b33b3102d210439338e1beea616a6122267c0df459ff0265cd5807a/asyncpg-0.31.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:334dec28cf20d7f5bb9e45b39546ddf247f8042a690bff9b9573d00086e69cb5", size = 638349, upload-time = "2025-11-24T23:26:19.689Z" },
{ url = "https://files.pythonhosted.org/packages/41/aa/e7f7ac9a7974f08eff9183e392b2d62516f90412686532d27e196c0f0eeb/asyncpg-0.31.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98cc158c53f46de7bb677fd20c417e264fc02b36d901cc2a43bd6cb0dc6dbfd2", size = 3410428, upload-time = "2025-11-24T23:26:21.275Z" },
{ url = "https://files.pythonhosted.org/packages/6f/de/bf1b60de3dede5c2731e6788617a512bc0ebd9693eac297ee74086f101d7/asyncpg-0.31.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9322b563e2661a52e3cdbc93eed3be7748b289f792e0011cb2720d278b366ce2", size = 3471678, upload-time = "2025-11-24T23:26:23.627Z" },
{ url = "https://files.pythonhosted.org/packages/46/78/fc3ade003e22d8bd53aaf8f75f4be48f0b460fa73738f0391b9c856a9147/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19857a358fc811d82227449b7ca40afb46e75b33eb8897240c3839dd8b744218", size = 3313505, upload-time = "2025-11-24T23:26:25.235Z" },
{ url = "https://files.pythonhosted.org/packages/bf/e9/73eb8a6789e927816f4705291be21f2225687bfa97321e40cd23055e903a/asyncpg-0.31.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ba5f8886e850882ff2c2ace5732300e99193823e8107e2c53ef01c1ebfa1e85d", size = 3434744, upload-time = "2025-11-24T23:26:26.944Z" },
{ url = "https://files.pythonhosted.org/packages/08/4b/f10b880534413c65c5b5862f79b8e81553a8f364e5238832ad4c0af71b7f/asyncpg-0.31.0-cp314-cp314-win32.whl", hash = "sha256:cea3a0b2a14f95834cee29432e4ddc399b95700eb1d51bbc5bfee8f31fa07b2b", size = 532251, upload-time = "2025-11-24T23:26:28.404Z" },
{ url = "https://files.pythonhosted.org/packages/d3/2d/7aa40750b7a19efa5d66e67fc06008ca0f27ba1bd082e457ad82f59aba49/asyncpg-0.31.0-cp314-cp314-win_amd64.whl", hash = "sha256:04d19392716af6b029411a0264d92093b6e5e8285ae97a39957b9a9c14ea72be", size = 604901, upload-time = "2025-11-24T23:26:30.34Z" },
{ url = "https://files.pythonhosted.org/packages/ce/fe/b9dfe349b83b9dee28cc42360d2c86b2cdce4cb551a2c2d27e156bcac84d/asyncpg-0.31.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bdb957706da132e982cc6856bb2f7b740603472b54c3ebc77fe60ea3e57e1bd2", size = 702280, upload-time = "2025-11-24T23:26:32Z" },
{ url = "https://files.pythonhosted.org/packages/6a/81/e6be6e37e560bd91e6c23ea8a6138a04fd057b08cf63d3c5055c98e81c1d/asyncpg-0.31.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6d11b198111a72f47154fa03b85799f9be63701e068b43f84ac25da0bda9cb31", size = 682931, upload-time = "2025-11-24T23:26:33.572Z" },
{ url = "https://files.pythonhosted.org/packages/a6/45/6009040da85a1648dd5bc75b3b0a062081c483e75a1a29041ae63a0bf0dc/asyncpg-0.31.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18c83b03bc0d1b23e6230f5bf8d4f217dc9bc08644ce0502a9d91dc9e634a9c7", size = 3581608, upload-time = "2025-11-24T23:26:35.638Z" },
{ url = "https://files.pythonhosted.org/packages/7e/06/2e3d4d7608b0b2b3adbee0d0bd6a2d29ca0fc4d8a78f8277df04e2d1fd7b/asyncpg-0.31.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e009abc333464ff18b8f6fd146addffd9aaf63e79aa3bb40ab7a4c332d0c5e9e", size = 3498738, upload-time = "2025-11-24T23:26:37.275Z" },
{ url = "https://files.pythonhosted.org/packages/7d/aa/7d75ede780033141c51d83577ea23236ba7d3a23593929b32b49db8ed36e/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3b1fbcb0e396a5ca435a8826a87e5c2c2cc0c8c68eb6fadf82168056b0e53a8c", size = 3401026, upload-time = "2025-11-24T23:26:39.423Z" },
{ url = "https://files.pythonhosted.org/packages/ba/7a/15e37d45e7f7c94facc1e9148c0e455e8f33c08f0b8a0b1deb2c5171771b/asyncpg-0.31.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8df714dba348efcc162d2adf02d213e5fab1bd9f557e1305633e851a61814a7a", size = 3429426, upload-time = "2025-11-24T23:26:41.032Z" },
{ url = "https://files.pythonhosted.org/packages/13/d5/71437c5f6ae5f307828710efbe62163974e71237d5d46ebd2869ea052d10/asyncpg-0.31.0-cp314-cp314t-win32.whl", hash = "sha256:1b41f1afb1033f2b44f3234993b15096ddc9cd71b21a42dbd87fc6a57b43d65d", size = 614495, upload-time = "2025-11-24T23:26:42.659Z" },
{ url = "https://files.pythonhosted.org/packages/3c/d7/8fb3044eaef08a310acfe23dae9a8e2e07d305edc29a53497e52bc76eca7/asyncpg-0.31.0-cp314-cp314t-win_amd64.whl", hash = "sha256:bd4107bb7cdd0e9e65fae66a62afd3a249663b844fa34d479f6d5b3bef9c04c3", size = 706062, upload-time = "2025-11-24T23:26:44.086Z" },
]
[[package]]
name = "dill"
version = "0.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" },
]
[[package]]
name = "greenlet"
version = "3.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a3/74/b13368064b09053253555d3f2839cc2684d22d5aed0d2ccffbf7a6736558/greenlet-3.5.4.tar.gz", hash = "sha256:0232ae1de90a8e07867bb127d7a6ba2301e859145489f25cda8a6096dabe1d20", size = 206538, upload-time = "2026-07-22T12:47:14.468Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/9a/e51225dcd58713f16ccbdcc501a8da21098ea14515b7870f1f94459e5ff5/greenlet-3.5.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:24e61b88cb7e1b1d794b32a10cc346ac779681d6d74ff137a3e0a444d2bf1f02", size = 294831, upload-time = "2026-07-22T11:38:53.389Z" },
{ url = "https://files.pythonhosted.org/packages/9f/ea/de50a50fadf979713ab18b46f22ad5ff5f2dcfc637a3ebdecf669801e1a5/greenlet-3.5.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:870d730fec833f5a06906a32596cc099b9161594642a92a520b7a88911c95356", size = 614619, upload-time = "2026-07-22T12:26:42.282Z" },
{ url = "https://files.pythonhosted.org/packages/db/c7/2aae27fea41205b8650294c301f042a2a4bb6155eea48c995b890a92f2c1/greenlet-3.5.4-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec5ff0d1878df6af3bf9b638a5a92a7d5693291de77c91bff10fa48519c604ef", size = 627021, upload-time = "2026-07-22T12:29:03.445Z" },
{ url = "https://files.pythonhosted.org/packages/eb/56/79fd826f9ccaae0b84e1b4ef68dabba5e105bb044ffcd448a0b782fcba9a/greenlet-3.5.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d84d993f6e575c950d91a23c1345d18fe1a4310d447bf630849d7809196b52f0", size = 624002, upload-time = "2026-07-22T11:51:11.391Z" },
{ url = "https://files.pythonhosted.org/packages/0a/1a/27319f97e731298513dcba1a2e91b63e9d8811d9de22130f960b129b1bf1/greenlet-3.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58023945f421093de5e6fa108c0985a8659d43f49e0216da25099369a121bcbd", size = 1581533, upload-time = "2026-07-22T12:25:05.322Z" },
{ url = "https://files.pythonhosted.org/packages/b1/6d/24240bf562e9786dd2799ee0a4a4dadb4ded22510f41b20245099159ac8c/greenlet-3.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bae2728e1897aa8df8cb1af38cd48b3a743aefe29372de7b8b7a9f532501e69f", size = 1645781, upload-time = "2026-07-22T11:51:14.805Z" },
{ url = "https://files.pythonhosted.org/packages/c1/5a/442ab1a9ef7ca6bf7210e5397a95972206a91a31033a03c8900866a10039/greenlet-3.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:ca5726c0b08ca35ae873557266a78b2c3f3b2b7d7401aa5ff886c2045dd0111c", size = 247133, upload-time = "2026-07-22T11:39:20.661Z" },
{ url = "https://files.pythonhosted.org/packages/3e/e6/9160210222386b1a378ff94db846b9508ca24a121cf684991561fdb69280/greenlet-3.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:7c1303791d603080cac6fc3b34df51c3b75b723739c282c8029e48a0d241672f", size = 245500, upload-time = "2026-07-22T11:40:22.185Z" },
{ url = "https://files.pythonhosted.org/packages/a5/a7/6ab1d4f9cd548d15ab90da29947f2076100130bb179b0bde59f795a459e3/greenlet-3.5.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:7e8afa5eac028f8140ceafe5ceec66e6aa127ddcb21452d2a564dcd2900b5f22", size = 295410, upload-time = "2026-07-22T11:40:35.747Z" },
{ url = "https://files.pythonhosted.org/packages/cd/7a/422f63b4715cbc0b24385305407adf38b48f6bb68b3e6b04090e994d0f5a/greenlet-3.5.4-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73b37afe369021423ea53dd3123e04bffa7e93ac64429b9f50835b2e4fcae7cf", size = 661286, upload-time = "2026-07-22T12:26:43.8Z" },
{ url = "https://files.pythonhosted.org/packages/d0/31/5a1cac663bf5582190c5a714ef81364f03cde232227f39748f8ae4c11da5/greenlet-3.5.4-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ef964f56dfcb6f9bbef2a190d9126795eac408716aeae47b5e7c73c32aafca9", size = 673517, upload-time = "2026-07-22T12:29:04.815Z" },
{ url = "https://files.pythonhosted.org/packages/15/4a/2a82a1e3f8aaca020853ac8d12211280ca2b231aa08ea39f636f1060c319/greenlet-3.5.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c53ff01a5c53a40f2c16820ebc56d7c61a77f5fbe009dadd96292d5682f80f8", size = 670917, upload-time = "2026-07-22T11:51:13.589Z" },
{ url = "https://files.pythonhosted.org/packages/68/b0/e379a152b17bfdfa95795af4049e37c0fd1b4d81f020d426db104ed07c77/greenlet-3.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecca4d80d55a01ad6b23b33262662956149fbb7b2c6be2910f1705921958cbf3", size = 1628478, upload-time = "2026-07-22T12:25:06.678Z" },
{ url = "https://files.pythonhosted.org/packages/5e/43/bffdfa64f7317f954c5c1230b5dd5922676ce198689a68c1ac1ed4b1b1a5/greenlet-3.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffbc533e0eaf8e80d8471411646ab88fe58f641d508c0b02b24494479f4d9ec", size = 1692021, upload-time = "2026-07-22T11:51:17.008Z" },
{ url = "https://files.pythonhosted.org/packages/d0/11/f799f9637e2c6e9b0b716015e339040598b058cf7654dfc0d67468b177ed/greenlet-3.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:305f69e6c4523d7f6979ed001cff4e5853c063e5da04880296603aa0227e544c", size = 248031, upload-time = "2026-07-22T11:40:11.007Z" },
{ url = "https://files.pythonhosted.org/packages/05/75/625bcdd74d5e6b2dca1ecba3c3ac77bcf8a026c21a649a46cef23e421f97/greenlet-3.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:f260930bbbbcf9caee661211235a5111c86dfe5832fdf6ae4570da1e0995320f", size = 246892, upload-time = "2026-07-22T11:40:27.357Z" },
{ url = "https://files.pythonhosted.org/packages/ec/69/35c62ed49c320cb4d98e14698ccca5467d3bfe683984172be9cb564d9ce3/greenlet-3.5.4-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:41ddab54e4b238f4a6c323f39b4e59e176affd5a94d461a9fb7583dac74240a3", size = 305571, upload-time = "2026-07-22T11:40:31.659Z" },
{ url = "https://files.pythonhosted.org/packages/5b/6c/64d60216b3640dcb0b62d913dd9e0d80030c09115bb2e4ba70c95d10ca45/greenlet-3.5.4-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3dabe3e2809013052c68bdf0b7fa5f5f2859c43a80803131ad61af9cabd7867", size = 672568, upload-time = "2026-07-22T12:26:45.298Z" },
{ url = "https://files.pythonhosted.org/packages/5c/de/ba3ab0a96292e53039530333b0d2ae18d9e508f3a325cd7bf15f8172944c/greenlet-3.5.4-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:27d3f00718634d4520a3a150154ac5da36f257869d41321953375b90bfbbc72c", size = 680076, upload-time = "2026-07-22T12:29:06.125Z" },
{ url = "https://files.pythonhosted.org/packages/a5/be/aeada79083c6f1c15f45d77a332f9c441af263ee298e3eb17522cd337d22/greenlet-3.5.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbd60b5763c6543c1827e48faaf14ea9bfbad245f52b1a4d76a2a2d8884c6c66", size = 676733, upload-time = "2026-07-22T11:51:16.027Z" },
{ url = "https://files.pythonhosted.org/packages/e9/10/2392fc3a98948652ef5fd1e7275c04f861dd13f74b78a2b4309f4ee4d090/greenlet-3.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f00f910f0e7b35416c63b23ad78b769aeccfc1775f712b43c4ee525624a2eef7", size = 1637327, upload-time = "2026-07-22T12:25:07.879Z" },
{ url = "https://files.pythonhosted.org/packages/55/c6/e7237a3dfa1f205ed0d9ea1e46d70bd2811b32d516266399fb59d28ab90a/greenlet-3.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:91c26423753b92caf41ab3f98fd547d7374d4d9fc2d85be041886c1579d9255e", size = 1697493, upload-time = "2026-07-22T11:51:19.214Z" },
{ url = "https://files.pythonhosted.org/packages/55/e3/4ba8154ba2a3d43729e499f72471b4b5c993f3826d3e24da81d5f06d6572/greenlet-3.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:ee032b91fd8ec29ec6c4cea2b8c561b178435134bd0752c7334b94e9c736c132", size = 251637, upload-time = "2026-07-22T11:40:37.44Z" },
{ url = "https://files.pythonhosted.org/packages/90/03/e3f96dfc100261a29545ddc8270cafe58f9195b6651466910e820910de77/greenlet-3.5.4-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:178111881dd7a6c946471fda85485ec796e1043c2b939f694b096e2ecf986809", size = 296076, upload-time = "2026-07-22T11:39:38.364Z" },
{ url = "https://files.pythonhosted.org/packages/a4/3d/da52d208e5c977bce8667e784729e584e38b5785f4c1ec0f4c836e9a1c42/greenlet-3.5.4-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d92df08dd65fede97fc37aad36c2e9dcda3b31c467f8e0c2c096456cb818e927", size = 666870, upload-time = "2026-07-22T12:26:46.691Z" },
{ url = "https://files.pythonhosted.org/packages/2d/8a/7e6dee25cb8a8cf9b362c8e597cc269593378bd916f16c736c059a52e85a/greenlet-3.5.4-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99e8f8c4ebc4fd80aa26c1280ae9ad43a0976e786349703a181cf0bae60413e5", size = 677678, upload-time = "2026-07-22T12:29:07.508Z" },
{ url = "https://files.pythonhosted.org/packages/6c/21/5a38699fa45de749e3857d93b8f07e4c20489e77c2d35d915a2e1c456606/greenlet-3.5.4-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:394de08dad5ffcb1f50c2159d93e398d9d2da3ed437645eaa54771fa720db9f0", size = 676067, upload-time = "2026-07-22T11:51:18.163Z" },
{ url = "https://files.pythonhosted.org/packages/5e/ba/863116ab8ff1ca7a729e327800268939d182db47aa433db70e216e7d9194/greenlet-3.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:c883d61f2282d72c767a14936641b3efcbde9d82f1080712aaea0b1d3126cb88", size = 1633489, upload-time = "2026-07-22T12:25:09.605Z" },
{ url = "https://files.pythonhosted.org/packages/fa/06/7466ced82818d6132462d7f26b3f83c66ea15d2b193a6c0088d558ed7d95/greenlet-3.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2a924f15d17957e252a810acefcb5942f5ca712298e8b6fcaed9a307d357522c", size = 1696584, upload-time = "2026-07-22T11:51:21.304Z" },
{ url = "https://files.pythonhosted.org/packages/f9/4d/55b638489260065de9ffce606c8b5d04507bef705de4b212a0c3d6a1a0df/greenlet-3.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:ed17e5f3420360d5b459de8462efb52060399a5326a613d4cde31cef63ef95da", size = 248297, upload-time = "2026-07-22T11:42:04.055Z" },
{ url = "https://files.pythonhosted.org/packages/bb/08/9dd4ae635da93d41dc268bc34bd62a9d711ed8b8825c5d22ac910c7d6e6d/greenlet-3.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:f908898d6fa484ce4b6f447ce70ea99b52c503fee419e53cf74d60a16bc9e667", size = 247423, upload-time = "2026-07-22T11:44:00.764Z" },
{ url = "https://files.pythonhosted.org/packages/19/66/7c87ed9cdbf1d49c2c6cd1c7b9dd4d16c33b24235ca03972293a1876b30c/greenlet-3.5.4-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:1833637f17d5e7472548a48575c394fe39f1b1890d676d162d86593610f44d8c", size = 306487, upload-time = "2026-07-22T11:41:25.118Z" },
{ url = "https://files.pythonhosted.org/packages/5b/05/0a4201e7c0054866eefc05da234f236dd4c950d0fbf9ca0517141f01b269/greenlet-3.5.4-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12cda9122e03341f1cb6b8207a19d7a9d375e52f1b4e9243918375f40fd7b4b9", size = 676479, upload-time = "2026-07-22T12:26:48.129Z" },
{ url = "https://files.pythonhosted.org/packages/3d/c0/4b6b8c5a3aec70f0649cd89662d120fdd6421e2bdc8e3b15c3ab5ec568d8/greenlet-3.5.4-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d83ae0e32d14957ab7170785a20f582635c8474deab1bfbb552b17e769a6ce25", size = 684321, upload-time = "2026-07-22T12:29:08.925Z" },
{ url = "https://files.pythonhosted.org/packages/24/c9/b49c31c9a972eee91e260445770e922244a7efc542697f51a012ec046d0f/greenlet-3.5.4-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f1467de1bb767f75db0aa34c195e3a496d8d1278c796e70c24ce205d3e99cde", size = 681293, upload-time = "2026-07-22T11:51:20.43Z" },
{ url = "https://files.pythonhosted.org/packages/95/6f/7f2d4653770500eee667866016d42d7a68e3d3462f80df6b8e3fcd48a0eb/greenlet-3.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0fa53040b78b578120eecdc0265e3f1051487cc425d11a2b7c761daadf4feaa8", size = 1642474, upload-time = "2026-07-22T12:25:10.819Z" },
{ url = "https://files.pythonhosted.org/packages/5a/d8/8cba31036a4caae448087ba5d150660ab03b4a0f54d9150f6495a3be7262/greenlet-3.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:60e0bc961d367df506660e9ac0177a76bc6d81305300704b0977d1634f76efe2", size = 1701012, upload-time = "2026-07-22T11:51:23.17Z" },
{ url = "https://files.pythonhosted.org/packages/e3/cd/3f77a4cce3bae631b08eb52f53a82a976669600337e21dfdba811cb50267/greenlet-3.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:f680e549edb3eaf21eea4e7fe101e15ec180c74b7879ab46adc080f22d4015d2", size = 251977, upload-time = "2026-07-22T11:41:38.125Z" },
{ url = "https://files.pythonhosted.org/packages/93/e8/65e8707d00fe2a49bf12f609a9b2b39ba6dd23c2810eacad877c4fc94bfe/greenlet-3.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:08fc36de8442d5c3e95b044550dbea9bf144d31ec0cc58e36fb241cb6ef6a994", size = 250538, upload-time = "2026-07-22T11:40:17.985Z" },
]
[[package]]
name = "inquirerpy"
version = "0.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pfzy" },
{ name = "prompt-toolkit" },
]
sdist = { url = "https://files.pythonhosted.org/packages/64/73/7570847b9da026e07053da3bbe2ac7ea6cde6bb2cbd3c7a5a950fa0ae40b/InquirerPy-0.3.4.tar.gz", hash = "sha256:89d2ada0111f337483cb41ae31073108b2ec1e618a49d7110b0d7ade89fc197e", size = 44431, upload-time = "2022-06-27T23:11:20.598Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/ff/3b59672c47c6284e8005b42e84ceba13864aa0f39f067c973d1af02f5d91/InquirerPy-0.3.4-py3-none-any.whl", hash = "sha256:c65fdfbac1fa00e3ee4fb10679f4d3ed7a012abf4833910e63c295827fe2a7d4", size = 67677, upload-time = "2022-06-27T23:11:17.723Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "kontor-robyn"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "asyncpg" },
{ name = "msgspec", extra = ["toml", "yaml"] },
{ name = "robyn", extra = ["all"] },
{ name = "sqlalchemy" },
]
[package.metadata]
requires-dist = [
{ name = "asyncpg", specifier = ">=0.31.0" },
{ name = "msgspec", extras = ["toml", "yaml"], specifier = ">=0.21.1" },
{ name = "robyn", extras = ["all"], specifier = ">=0.88.0" },
{ name = "sqlalchemy", specifier = ">=2.0.51" },
]
[[package]]
name = "markupsafe"
version = "3.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
[[package]]
name = "msgspec"
version = "0.21.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/60/f79b9b013a16fa3a58350c9295ddc6789f2e335f36ea61ed10a21b215364/msgspec-0.21.1.tar.gz", hash = "sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c", size = 319193, upload-time = "2026-04-12T21:44:50.394Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/74/f11ede02839b19ff459f88e3145df5d711626ca84da4e23520cebf819367/msgspec-0.21.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:764173717a01743f007e9f74520ed281f24672c604514f7d76c1c3a10e8edb66", size = 196176, upload-time = "2026-04-12T21:44:17.613Z" },
{ url = "https://files.pythonhosted.org/packages/bb/40/4476c1bd341418a046c4955aff632ec769315d1e3cb94e6acf86d461f9ed/msgspec-0.21.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:344c7cd0eaed1fb81d7959f99100ef71ec9b536881a376f11b9a6c4803365697", size = 188524, upload-time = "2026-04-12T21:44:18.815Z" },
{ url = "https://files.pythonhosted.org/packages/ca/d9/9e9d7d7e5061b47540d03d640fab9b3965ba7ae49c1b2154861c8f007518/msgspec-0.21.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48943e278b3854c2f89f955ddc6f9f430d3f0784b16e47d10604ee0463cd21f5", size = 218880, upload-time = "2026-04-12T21:44:20.028Z" },
{ url = "https://files.pythonhosted.org/packages/74/66/2bb344f34abb4b57e60c7c9c761994e0417b9718ec1460bf00c296f2a7ea/msgspec-0.21.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9aa659ebb0101b1cbc31461212b87e341d961f0ab0772aaf068a99e001ec4aa", size = 225050, upload-time = "2026-04-12T21:44:21.577Z" },
{ url = "https://files.pythonhosted.org/packages/1a/84/7c1e412f76092277bf760cef12b7979d03314d259ab5b5cafde5d0c1722d/msgspec-0.21.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7b27d1a8ead2b6f5b0c4f2d07b8be1ccfcc041c8a0e704781edebe3ae13c484", size = 222713, upload-time = "2026-04-12T21:44:22.83Z" },
{ url = "https://files.pythonhosted.org/packages/4e/27/0bba04b2b4ef05f3d068429410bc71d2cea925f1596a8f41152cccd5edb8/msgspec-0.21.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38fe93e86b61328fe544cb7fd871fad5a27c8734bfda90f65e5dbe288ae50f61", size = 227259, upload-time = "2026-04-12T21:44:24.11Z" },
{ url = "https://files.pythonhosted.org/packages/b0/2d/09574b0eea02fed2c2c1383dbaae2c7f79dc16dcd6487a886000afb5d7c4/msgspec-0.21.1-cp313-cp313-win_amd64.whl", hash = "sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a", size = 189857, upload-time = "2026-04-12T21:44:25.359Z" },
{ url = "https://files.pythonhosted.org/packages/46/34/105b1576ad182879914f0c821f17ee1d13abb165cb060448f96fe2aff078/msgspec-0.21.1-cp313-cp313-win_arm64.whl", hash = "sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898", size = 175403, upload-time = "2026-04-12T21:44:26.608Z" },
{ url = "https://files.pythonhosted.org/packages/5a/ad/86954e987d1d6a5c579e2c2e7832b65e0fff194179fdac4f581536086024/msgspec-0.21.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fab48eb45fdbfbdb2c0edfec00ffc53b6b6085beefc6b50b61e01659f9f8757f", size = 196261, upload-time = "2026-04-12T21:44:27.807Z" },
{ url = "https://files.pythonhosted.org/packages/d1/a1/c5e46c3e42b866199365e35d11dddfd1fbd8bba4fdb3c52f965b1607ce94/msgspec-0.21.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3cb779ea0c35bc807ff941d415875c1f69ca0be91a2e907ab99a171811d86a9a", size = 188729, upload-time = "2026-04-12T21:44:28.99Z" },
{ url = "https://files.pythonhosted.org/packages/85/7d/1e29a319d678d6cb962ae5bdf32a6858ebdf38f73bc654c0e9c742a0c2c8/msgspec-0.21.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68604db36b3b4dd9bf160e436e12798a4738848144cea1aca1cb984011eb160f", size = 219866, upload-time = "2026-04-12T21:44:31.104Z" },
{ url = "https://files.pythonhosted.org/packages/25/1f/cca084ca2572810fff12ea9dbdcbe39eac048f40daf4a9077b49fcbe8cee/msgspec-0.21.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d6b9dc50948eaf65df54d2fd0ff66e6d8c32f116037209ee861810eb9b676cb", size = 224993, upload-time = "2026-04-12T21:44:32.649Z" },
{ url = "https://files.pythonhosted.org/packages/71/94/d2120fc9d419a89a3a7c13e5b7078798c4b392a96a02a6e2b3ce43a8766c/msgspec-0.21.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:52c5e21930942302394429c5a582ce7e6b62c7f983b3760834c2ce107e0dd6df", size = 223535, upload-time = "2026-04-12T21:44:33.839Z" },
{ url = "https://files.pythonhosted.org/packages/75/17/42418b66a3ad972a89bab73dd78b79cc6282bb488a25e73c853cee7443b9/msgspec-0.21.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:abbb39d65681fa24ed394e01af3d59d869068324f900c61d06062b7fb9980f2f", size = 227222, upload-time = "2026-04-12T21:44:35.093Z" },
{ url = "https://files.pythonhosted.org/packages/c4/33/265c894268cca88ff67b144ca2b4c522fc8b9a6f1966a3640c70516e78e1/msgspec-0.21.1-cp314-cp314-win_amd64.whl", hash = "sha256:5666b1b560b97b6ec2eb3fca8a502298ebac56e13bbca1f88523538ce83d01ea", size = 193810, upload-time = "2026-04-12T21:44:36.612Z" },
{ url = "https://files.pythonhosted.org/packages/3b/8f/a6d35f25bf1fc63c492fdd88fdce01ba0875ead48c2b91f90f33653b4131/msgspec-0.21.1-cp314-cp314-win_arm64.whl", hash = "sha256:d8b8578e4c83b14ceea4cef0d0b747e31d9330fe4b03b2b2ad4063866a178f93", size = 179125, upload-time = "2026-04-12T21:44:38.198Z" },
{ url = "https://files.pythonhosted.org/packages/c6/39/74839641e64b99d87da55af0fc472854d42b46e2183b9e2a67fe1bb2a512/msgspec-0.21.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15f523d51c00ebad412213bfe9f06f0a50ec2b93e0c19e824a2d267cabb48ea2", size = 200171, upload-time = "2026-04-12T21:44:39.414Z" },
{ url = "https://files.pythonhosted.org/packages/70/9b/ce0cca6d2d87fcd4b6ff97600790494e64f26a2c55d61507cd2755c16193/msgspec-0.21.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e47390360583ba3d5c6cb44cf0a9f61b0a06a899d3c2c00627cedebb2e2884b", size = 192879, upload-time = "2026-04-12T21:44:40.882Z" },
{ url = "https://files.pythonhosted.org/packages/a7/08/673a7bb05e5702dc787ddd3011195b509f9867927970da59052211929987/msgspec-0.21.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f60800e6299b798142dc40b0644da77ceac5ea0568be58228417eae14135c847", size = 226281, upload-time = "2026-04-12T21:44:42.181Z" },
{ url = "https://files.pythonhosted.org/packages/7d/45/86508cf57283e9070b3c447e3ab25b792a7a0855a3ea4e0c6d111ac34c97/msgspec-0.21.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5f8e9dfcd98419cf7568808470c4317a3fb30bef0e3715b568730a2b272a20d7", size = 229863, upload-time = "2026-04-12T21:44:43.442Z" },
{ url = "https://files.pythonhosted.org/packages/2c/62/e7c9367cd08d590559faacd711edbae36840342843e669440363f33c7d36/msgspec-0.21.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92d89dfad13bd1ea640dc3e37e724ed380da1030b272bdf5ecafb983c3ad7c75", size = 230445, upload-time = "2026-04-12T21:44:44.806Z" },
{ url = "https://files.pythonhosted.org/packages/42/b4/c0f54632103846b658a10930025f4de41c8724b5e4805a5f3b395586cb7e/msgspec-0.21.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0d03867786e5d7ba25d666df4b11320c27170f4aeafcb8e3a8b0a50a4fb742ca", size = 231822, upload-time = "2026-04-12T21:44:46.343Z" },
{ url = "https://files.pythonhosted.org/packages/ea/1d/0d85cc79d0ccf5508e9c846cc66552a6a16bf92abd1dbd8362617f7b35cd/msgspec-0.21.1-cp314-cp314t-win_amd64.whl", hash = "sha256:740fbf1c9d59992ca3537d6fbe9ebbf9eaf726a65fbf31448e0ecbc710697a63", size = 206650, upload-time = "2026-04-12T21:44:47.601Z" },
{ url = "https://files.pythonhosted.org/packages/90/91/56c5d560f20e6c20e9e4f55bd0e458f7f162aa689ee350346c04c48eac0b/msgspec-0.21.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0d2cc73df6058d811a126ac3a8ad63a4dfa210c82f9cf5a004802eaf4712de90", size = 183149, upload-time = "2026-04-12T21:44:48.833Z" },
]
[package.optional-dependencies]
toml = [
{ name = "tomli-w" },
]
yaml = [
{ name = "pyyaml" },
]
[[package]]
name = "multiprocess"
version = "0.70.19"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "dill" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" },
{ url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" },
{ url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" },
{ url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" },
{ url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318, upload-time = "2026-01-19T06:47:37.497Z" },
{ url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" },
]
[[package]]
name = "orjson"
version = "3.11.9"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz", hash = "sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", size = 5599163, upload-time = "2026-05-06T15:11:08.309Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/33/93fcc25907235c344ae73122f8a4e01d2d393ef062b4af7d2e2487a32c37/orjson-3.11.9-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021", size = 228458, upload-time = "2026-05-06T15:10:20.079Z" },
{ url = "https://files.pythonhosted.org/packages/8f/27/b1e6dadb3c080313c03fdd8067b85e6a0460c7d8d6a1c3984ef77b904e4d/orjson-3.11.9-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c", size = 128368, upload-time = "2026-05-06T15:10:21.549Z" },
{ url = "https://files.pythonhosted.org/packages/21/0f/c9ede0bf052f6b4051e64a7d4fa91b725cccf8321a6a786e86eb03519f00/orjson-3.11.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81", size = 132070, upload-time = "2026-05-06T15:10:23.371Z" },
{ url = "https://files.pythonhosted.org/packages/fd/26/d398e28048dc18205bbe812f2c88cb9b40313db2470778e25964796458fe/orjson-3.11.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a", size = 127892, upload-time = "2026-05-06T15:10:24.714Z" },
{ url = "https://files.pythonhosted.org/packages/66/60/52b0054c4c700d5aa7fc5b7ca96917400d8f061307778578e67a10e25852/orjson-3.11.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10", size = 135217, upload-time = "2026-05-06T15:10:26.084Z" },
{ url = "https://files.pythonhosted.org/packages/d5/97/1e3dc2b2a28b7b2528f403d2fc1d79ec5f39af3bc143ab65d3ec26426385/orjson-3.11.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362", size = 145980, upload-time = "2026-05-06T15:10:28.062Z" },
{ url = "https://files.pythonhosted.org/packages/fc/39/31fbfe7850f2de32dee7e7e5c09f26d403ab01e440ac96001c6b01ad3c99/orjson-3.11.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97", size = 132738, upload-time = "2026-05-06T15:10:29.727Z" },
{ url = "https://files.pythonhosted.org/packages/a1/08/dca0082dd2a194acb93e5457e73455388e2e2ca464a2672449a9ddbb679d/orjson-3.11.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218", size = 134033, upload-time = "2026-05-06T15:10:31.152Z" },
{ url = "https://files.pythonhosted.org/packages/11/d4/5bdb0626801230139987385554c5d4c42255218ac906525bf4347f22cd95/orjson-3.11.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9", size = 141492, upload-time = "2026-05-06T15:10:32.641Z" },
{ url = "https://files.pythonhosted.org/packages/fa/88/a21fb53b3ede6703aede6dce4710ed4111e5b201cfa6bbff5e544f9d47d7/orjson-3.11.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677", size = 415087, upload-time = "2026-05-06T15:10:34.438Z" },
{ url = "https://files.pythonhosted.org/packages/3d/57/1b30daf70f0d8180e9a73cefbfbdd99e4bf19eb020466502b01fba7e0e50/orjson-3.11.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4", size = 148031, upload-time = "2026-05-06T15:10:36.358Z" },
{ url = "https://files.pythonhosted.org/packages/04/83/45fbb6d962e260807f99441db9613cee868ceda4baceda59b3720a563f97/orjson-3.11.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0", size = 136915, upload-time = "2026-05-06T15:10:38.013Z" },
{ url = "https://files.pythonhosted.org/packages/5f/cc/2d10025f9056d376e4127ec05a5808b218d46f035fdc08178a5411b34250/orjson-3.11.9-cp313-cp313-win32.whl", hash = "sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32", size = 131613, upload-time = "2026-05-06T15:10:39.569Z" },
{ url = "https://files.pythonhosted.org/packages/67/bd/2775ff28bfe883b9aa1ff348300542eb2ef1ee18d8ae0e3a49846817a865/orjson-3.11.9-cp313-cp313-win_amd64.whl", hash = "sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979", size = 127086, upload-time = "2026-05-06T15:10:41.262Z" },
{ url = "https://files.pythonhosted.org/packages/91/2b/d26799e580939e32a7da9a39531bc9e58e15ca32ffaa6a8cb3e9bb0d22cd/orjson-3.11.9-cp313-cp313-win_arm64.whl", hash = "sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254", size = 126696, upload-time = "2026-05-06T15:10:42.651Z" },
{ url = "https://files.pythonhosted.org/packages/8e/eb/5da01e356015aee6ecfa1187ced87aef51364e306f5e695dd52719bf0e78/orjson-3.11.9-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e", size = 228465, upload-time = "2026-05-06T15:10:44.097Z" },
{ url = "https://files.pythonhosted.org/packages/64/62/3e0e0c14c957133bcd855395c62b55ed4e3b0af23ffea11b032cb1dcbdb1/orjson-3.11.9-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e", size = 128364, upload-time = "2026-05-06T15:10:45.839Z" },
{ url = "https://files.pythonhosted.org/packages/5a/5a/07d8aa117211a8ed7630bda80c8c0b14d04e0f8dcf99bcf49656e4a710eb/orjson-3.11.9-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0", size = 132063, upload-time = "2026-05-06T15:10:47.267Z" },
{ url = "https://files.pythonhosted.org/packages/d6/ec/4acaf21483e18aa945be74a474c74b434f284b549f275a0a39b9f98956e9/orjson-3.11.9-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124", size = 122356, upload-time = "2026-05-06T15:10:48.765Z" },
{ url = "https://files.pythonhosted.org/packages/13/d8/5f0555e7638801323b7a75850f92e7dfa891bc84fe27a1ba4449170d1200/orjson-3.11.9-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c", size = 129592, upload-time = "2026-05-06T15:10:50.13Z" },
{ url = "https://files.pythonhosted.org/packages/b6/30/ed9860412a3603ceb3c5955bfd72d28b9d0e7ba6ed81add14f83d7114236/orjson-3.11.9-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7", size = 140491, upload-time = "2026-05-06T15:10:51.582Z" },
{ url = "https://files.pythonhosted.org/packages/d0/17/adc514dea7ac7c505527febf884934b815d34f0c7b8693c1a8b39c5c4a57/orjson-3.11.9-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1", size = 127309, upload-time = "2026-05-06T15:10:53.329Z" },
{ url = "https://files.pythonhosted.org/packages/76/3e/c0b690253f0b82d86e99949af13533363acfb5432ecb5d53dd5b3bce9c34/orjson-3.11.9-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db", size = 134030, upload-time = "2026-05-06T15:10:54.988Z" },
{ url = "https://files.pythonhosted.org/packages/c1/7a/bc82a0bb25e9faaf92dc4d9ef002732efc09737706af83e346788641d4a7/orjson-3.11.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b", size = 141482, upload-time = "2026-05-06T15:10:56.663Z" },
{ url = "https://files.pythonhosted.org/packages/01/55/e69188b939f77d5d32a9833745ace31ea5ccae3ab613a1ec185d3cd2c4fb/orjson-3.11.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972", size = 415178, upload-time = "2026-05-06T15:10:58.446Z" },
{ url = "https://files.pythonhosted.org/packages/2e/1a/b8a5a7ac527e80b9cb11d51e3f6689b709279183264b9ec5c7bc680bb8b5/orjson-3.11.9-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0", size = 148089, upload-time = "2026-05-06T15:11:00.441Z" },
{ url = "https://files.pythonhosted.org/packages/97/4e/00503f64204bf859b37213a63927028f30fb6268cd8677fb0a5ad48155e1/orjson-3.11.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586", size = 136921, upload-time = "2026-05-06T15:11:02.176Z" },
{ url = "https://files.pythonhosted.org/packages/0d/ba/a23b82a0a8d0ed7bed4e5f5035aae751cad4ff6a1e8d2ecd14d8860f5929/orjson-3.11.9-cp314-cp314-win32.whl", hash = "sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673", size = 131638, upload-time = "2026-05-06T15:11:03.696Z" },
{ url = "https://files.pythonhosted.org/packages/f3/c3/0c6798456bade745c75c452342dabacce5798196483e77e643be1f53877d/orjson-3.11.9-cp314-cp314-win_amd64.whl", hash = "sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b", size = 127078, upload-time = "2026-05-06T15:11:05.123Z" },
{ url = "https://files.pythonhosted.org/packages/16/21/5a3f1e8913103b703a436a5664238e5b965ec392b555fe68943ea3691e6b/orjson-3.11.9-cp314-cp314-win_arm64.whl", hash = "sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9", size = 126687, upload-time = "2026-05-06T15:11:06.602Z" },
]
[[package]]
name = "pfzy"
version = "0.3.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d9/5a/32b50c077c86bfccc7bed4881c5a2b823518f5450a30e639db5d3711952e/pfzy-0.3.4.tar.gz", hash = "sha256:717ea765dd10b63618e7298b2d98efd819e0b30cd5905c9707223dceeb94b3f1", size = 8396, upload-time = "2022-01-28T02:26:17.946Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/d7/8ff98376b1acc4503253b685ea09981697385ce344d4e3935c2af49e044d/pfzy-0.3.4-py3-none-any.whl", hash = "sha256:5f50d5b2b3207fa72e7ec0ef08372ef652685470974a107d0d4999fc5a903a96", size = 8537, upload-time = "2022-01-28T02:26:16.047Z" },
]
[[package]]
name = "prompt-toolkit"
version = "3.0.53"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "wcwidth" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" },
]
[[package]]
name = "pydantic"
version = "2.13.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
]
[[package]]
name = "pydantic-core"
version = "2.46.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
{ url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
{ url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
{ url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
{ url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
{ url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
{ url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
{ url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
{ url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
{ url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
{ url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
{ url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
{ url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
{ url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
{ url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
{ url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
{ url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
{ url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
{ url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
{ url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
{ url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
{ url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
{ url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
{ url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
{ url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
{ url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
{ url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
{ url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
{ url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
{ url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
{ url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
{ url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
{ url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
{ url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
{ url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
{ url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
{ url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
{ url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
{ url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
{ url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
{ url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
{ url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "robyn"
version = "0.88.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "inquirerpy" },
{ name = "multiprocess" },
{ name = "orjson" },
{ name = "rustimport" },
{ name = "uvloop", marker = "platform_machine != 'armv7l' and platform_python_implementation == 'CPython' and sys_platform != 'win32'" },
{ name = "watchdog" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/cd/a3/c60e52b82a57b3551e9b283a2f583da39276d4526b70a89a0c34c17f999e/robyn-0.88.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:47ff0ab0c99b96cdc57f66843f2875cfb59d8ec58c5ec2277e36795c6ff9fc16", size = 3375497, upload-time = "2026-06-25T03:09:12.491Z" },
{ url = "https://files.pythonhosted.org/packages/7f/79/11bfac75c4e0ad5a71e9f28d5fa444e469ba8543d65b96ebcd28005ef056/robyn-0.88.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3e23102dd820e6f9e1f78929e68111920c1d0d8f361d7afe36dffc8734c1456c", size = 1816052, upload-time = "2026-06-25T03:09:14.19Z" },
{ url = "https://files.pythonhosted.org/packages/56/62/7d3eca16ec0fa9e8c30d3ff408ebc4a0066d56b482dd76fdbd8ea67063e6/robyn-0.88.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5fed59aff389ace14f103421832329d66831b26ace6a03fe3fe6eb610e6437da", size = 1947507, upload-time = "2026-06-25T03:09:15.665Z" },
{ url = "https://files.pythonhosted.org/packages/ad/73/ddfa30e6b0f209bb8c8734461a565ffc6e793939b36e9919f6776e462091/robyn-0.88.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32020972ae2a149fba92a9c8ee4fe630d27c9cda7ee34e99bb3ac2a0add2e8c5", size = 1860242, upload-time = "2026-06-25T03:09:17.178Z" },
{ url = "https://files.pythonhosted.org/packages/cd/6a/58100fddcf239a9e6a6addad90ee85ca1b563bdb5364bbd835acdbd9b289/robyn-0.88.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e220b0438313e15c299c2233335b7167418694c2f08370805c369d5506adba05", size = 1889553, upload-time = "2026-06-25T03:09:18.792Z" },
{ url = "https://files.pythonhosted.org/packages/91/84/17f82b338179b17a816c98b2fc107317f3ed803f41bb277252bbe91af52c/robyn-0.88.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf61564c3ca82b4d3e36b4c59d3ff3323dfc7fbeea9ad65de5ab21a2501ffd5", size = 1934487, upload-time = "2026-06-25T03:09:20.319Z" },
{ url = "https://files.pythonhosted.org/packages/64/5f/de99615c6dabb6d604673fb2939e8a8ceddba155f424f7468ebbb47c8574/robyn-0.88.0-cp313-cp313-win32.whl", hash = "sha256:1d72a0d063fa439a4cb03f43e29e1f6e5f3d676beea67ea136c2dec6377fd73c", size = 1683060, upload-time = "2026-06-25T03:09:21.989Z" },
{ url = "https://files.pythonhosted.org/packages/f9/40/04fd7d414e05dce1a8ef44a01e7f3ac3c813721d869b6610d35bdb53aa26/robyn-0.88.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0c4801984a220ed50467bc95297474644ab13d4175da3288d48dda11ab172d9", size = 1751455, upload-time = "2026-06-25T03:09:23.684Z" },
{ url = "https://files.pythonhosted.org/packages/9f/44/c781b31b548305ccfa5657ef7260ff3b83d607b4dcebe64238c197945c29/robyn-0.88.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8f4846f848570b3c1cda029262a3bbd1b2e6e26ff8b6e049e7db7383223e749b", size = 3378970, upload-time = "2026-06-25T03:09:25.523Z" },
{ url = "https://files.pythonhosted.org/packages/f2/9c/26d44d07069993cc02d5152f726e5888624376a7a499df2e8d022a1e0689/robyn-0.88.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1ed30c0612b93315e4476d372be665530669d3291d40e21ce0aba1542a8d151f", size = 1818120, upload-time = "2026-06-25T03:09:27.437Z" },
{ url = "https://files.pythonhosted.org/packages/25/c8/5d8deba0b4a9331a7faec7f6037669d64a3afd9c8ecdf49081cead56c5f0/robyn-0.88.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:44d63f1406ed4c98540c81f508a3054b32c9cb32bb8f87af2572f87ed97a75b2", size = 1949312, upload-time = "2026-06-25T03:09:29.505Z" },
{ url = "https://files.pythonhosted.org/packages/a5/43/23d86e451e4d2e8d4146c926e0ad3c577d3ceb7bf8c9ba46a7f3f6682940/robyn-0.88.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce5799ff4ee074b310274f9da8577b1f76dbf7569ccaa64b3426f7a71ab5b2b4", size = 1936436, upload-time = "2026-06-25T03:09:31.232Z" },
{ url = "https://files.pythonhosted.org/packages/fb/2b/a69145da54824100e992e383540c1f5ce2107af05b57678985c1e9ffcca6/robyn-0.88.0-cp314-cp314-win32.whl", hash = "sha256:18636a6c281c11cd04da57f5bf736af0c8ac795e42b6eef828a03c2f7d91f178", size = 1685477, upload-time = "2026-06-25T03:09:32.872Z" },
{ url = "https://files.pythonhosted.org/packages/94/ec/c294dff747ce717fceb1458b1b4240bb57dbb04e1ce96392486da2f9bb91/robyn-0.88.0-cp314-cp314-win_amd64.whl", hash = "sha256:06f3bfdc877dbadfe37bb9ee658e72fd60751954f6079f2c884318a0cc95519e", size = 1755139, upload-time = "2026-06-25T03:09:35.018Z" },
]
[package.optional-dependencies]
all = [
{ name = "jinja2" },
{ name = "pydantic" },
]
[[package]]
name = "rustimport"
version = "1.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "toml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/83/090f4be41dfbfd120a5fe0ed82b1857083bafe0928e876480d72a63e8bf3/rustimport-1.3.4.tar.gz", hash = "sha256:ba80e3c28af07ba3910ad395613d01f9e421bfb59fbb1ac050e2b5d9b78b4980", size = 28817, upload-time = "2023-07-13T14:52:23.479Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/e6/376cc02c6ec2dd29de14225bd322ea742b0b864c05c62abe40208ce64ebd/rustimport-1.3.4-py3-none-any.whl", hash = "sha256:f2b931ff4e0fa931028066a7dacaae449b1a4601fe7a553c35f3dd63aba97ce0", size = 26341, upload-time = "2023-07-13T14:52:21.789Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.51"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" },
{ url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" },
{ url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" },
{ url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" },
{ url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" },
{ url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" },
{ url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" },
{ url = "https://files.pythonhosted.org/packages/b1/49/a739be2e1d02a96a658eb71ab45d921c874249252358ad24a5bffdd02525/sqlalchemy-2.0.51-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ea306caaae6bd5afd0a46050003c88f6bf33227377a49298c498c3cb88ff491", size = 2158999, upload-time = "2026-06-15T16:08:51.759Z" },
{ url = "https://files.pythonhosted.org/packages/23/6b/2e0e38cf75c8780eca78d9b2e78164f8bcfd70125e5caa588ff5cbb9c9f4/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c45a496d6bc05dec41dcd4c3a2b183723f47473255c159cd80b503c8f246424d", size = 3282539, upload-time = "2026-06-15T16:19:51.065Z" },
{ url = "https://files.pythonhosted.org/packages/dd/a1/e77854cb5336fd37dc3c6ae3b71de242c98caac5725120be0b526b31cbd0/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4004ada0aafe8ae1991b2cd1d99c6d9146126e123bd6f883c260d974aa012e54", size = 3287545, upload-time = "2026-06-15T16:26:44.735Z" },
{ url = "https://files.pythonhosted.org/packages/f6/ab/9e17272fd4dac8df3b83c4fbe52b998a1c9d89a843c8c35ff29b74ff7364/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f6bcad487aee1c638d707235682fc96f741de00663619881ab235400d03289e", size = 3230929, upload-time = "2026-06-15T16:19:52.625Z" },
{ url = "https://files.pythonhosted.org/packages/02/3c/52f408ea701781caee975606beccc48845f2aee8711ac29843d612c0306c/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:39a76529db6305693d8d4affa58ad5b5e2e18edd62daea628b29b97930b3513d", size = 3252888, upload-time = "2026-06-15T16:26:46.454Z" },
{ url = "https://files.pythonhosted.org/packages/24/16/3efd2ee6bc4ca4693a30a1dd17a91b606cae15d517d2a4746611d9b73ce8/sqlalchemy-2.0.51-cp314-cp314-win32.whl", hash = "sha256:08a204d8b5638717c26a24df18fcf40af45a6b22e35b70b1d62f0113c2e278e8", size = 2120551, upload-time = "2026-06-15T16:23:15.629Z" },
{ url = "https://files.pythonhosted.org/packages/7b/78/55b12e70f45bccc40d9e483925c065027b3b98ea4cbbdf6f8c2546feaf6c/sqlalchemy-2.0.51-cp314-cp314-win_amd64.whl", hash = "sha256:96747bfbadb055466e5b46d572618170046b45ce5a4879167f50d70a5319a499", size = 2146318, upload-time = "2026-06-15T16:23:17.108Z" },
{ url = "https://files.pythonhosted.org/packages/21/db/a9574ed40fed418924b1b1a3e54f47ee3963053b3d3d325a0d36b41f2c08/sqlalchemy-2.0.51-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1a213be1fcd5e49d9904c3b9939211ded90bc2a64e93f4c01963474285de", size = 2178920, upload-time = "2026-06-15T15:59:56.285Z" },
{ url = "https://files.pythonhosted.org/packages/bf/90/a1bb5c7cbba76b7bc1fbd586d0a5479a7bc9c27b4a8298f22ec9423b2bb3/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c6b36ed71f41942bdcd2ad2522be46bfce09d5705be5640ecf19bbc7660e4b7", size = 3566534, upload-time = "2026-06-15T15:58:35.024Z" },
{ url = "https://files.pythonhosted.org/packages/15/4b/481f1fed30e0e9e8dd24aecbb49f29eb57fe7657ece5cf06ee9b84bb97d8/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c2c62877097e1a0db401fba5cb4debee33265e5b2a55c4ccb489c02c53b4f72", size = 3535844, upload-time = "2026-06-15T16:02:43.973Z" },
{ url = "https://files.pythonhosted.org/packages/02/71/0aa64aeda645510af0a43f7d9ee70932f0d1dc4263aed34c50ee891d9df3/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0378d055e9e8cd6ce4d8dff683bdd3d7d413533c4ee51d67a2b1e0f9eacc0f23", size = 3475355, upload-time = "2026-06-15T15:58:36.592Z" },
{ url = "https://files.pythonhosted.org/packages/05/db/6061db32316446135a3abae5f308d144ab988a34234726042da3e58b1c63/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e46fc36029eff666391e0531e5387b62ce6c4f1d8e50b3fb3099eaca1b42522", size = 3486591, upload-time = "2026-06-15T16:02:45.346Z" },
{ url = "https://files.pythonhosted.org/packages/0d/c9/f14fdf71bb8957e0c7e39db69bbdf12b5c80f4ef775fdfa127bf4e0d6760/sqlalchemy-2.0.51-cp314-cp314t-win32.whl", hash = "sha256:9161cfc9efce70d1715f47d6ff40f79c6778c00d53be4fbc09d70301e4b83ba7", size = 2151313, upload-time = "2026-06-15T16:03:39.127Z" },
{ url = "https://files.pythonhosted.org/packages/6a/c6/673e618e6f4f297e126d9b56ea2f6478708f6c1af4e3223835c22e2c3697/sqlalchemy-2.0.51-cp314-cp314t-win_amd64.whl", hash = "sha256:159bb6ba32059f57ad7375a8f50d844dd2f19d14954ecf820cd33e20debd46b2", size = 2186280, upload-time = "2026-06-15T16:03:40.569Z" },
{ url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" },
]
[[package]]
name = "toml"
version = "0.10.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
]
[[package]]
name = "tomli-w"
version = "1.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
]
[[package]]
name = "typing-extensions"
version = "4.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]
[[package]]
name = "uvloop"
version = "0.22.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" },
{ url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" },
{ url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" },
{ url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" },
{ url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" },
{ url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" },
{ url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" },
{ url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" },
{ url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" },
{ url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" },
{ url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" },
{ url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" },
{ url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" },
{ url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" },
{ url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" },
{ url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" },
{ url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
]
[[package]]
name = "watchdog"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
]
[[package]]
name = "wcwidth"
version = "0.8.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" },
]
+46 -29
View File
@@ -1,61 +1,77 @@
"""
read file with URLs and store in DB
"""
import json
import logging.config
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
from pathlib import Path
import requests
import yaml
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from pathlib import Path
from platformdirs import PlatformDirs
from proton import Message, Event
from proton import Event, Message
from proton.handlers import MessagingHandler
from proton.reactor import Container
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('-u', '--url', help='link')
parser.add_argument('--video', help='store Url as VideoFile', action="store_true")
parser.add_argument("-u", "--url", help="link")
parser.add_argument("--video", help="store Url as VideoFile", action="store_true")
parser.add_argument("--api", help="use Kontor API", action="store_true")
parser.add_argument('--config', '-c', default='kontor-docker')
parser.add_argument('--verbose', '-v', action='count', default=0)
parser.add_argument("--config", "-c", default="kontor-docker")
parser.add_argument("--verbose", "-v", action="count", default=0)
parser.add_argument("--server", "-s", default="127.0.0.1")
args = parser.parse_args()
def get_logger(level: int, config: str):
"""
create Logger with configuration from config file
"""
dirs = PlatformDirs(config)
logging_config = Path(dirs.user_config_dir, 'logging-config.yaml')
with open(logging_config, 'rt') as f:
configDict = yaml.safe_load(f.read())
logging.config.dictConfig(configDict)
logger = logging.getLogger('development')
logging_config = Path(dirs.user_config_dir, "logging-config.yaml")
with open(logging_config, "rt", encoding="UTF-8") as f:
config_dict = yaml.safe_load(f.read())
logging.config.dictConfig(config_dict)
log = logging.getLogger("development")
if level is not None:
match level:
case 0:
logger.setLevel(logging.INFO)
log.setLevel(logging.INFO)
case 1:
logger.setLevel(logging.DEBUG)
log.setLevel(logging.DEBUG)
case _:
logger.setLevel(logging.CRITICAL)
return logger
log.setLevel(logging.CRITICAL)
return log
class AddLinkMessage(MessagingHandler):
def __init__(self, server, url, log):
"""
Create message for queue add_link_file
"""
def __init__(self, server, message, log):
super(AddLinkMessage, self).__init__()
log.info("create AddLinkMessage")
self.server = server
self.address = "add_link_file"
self.url = url
self.address = "media.link.add"
self.message = message
self.log = log
def on_start(self, event: Event):
def on_start(self, event):
self.log.info("Connection...")
conn = event.container.connect(self.server, user="artemis", password="artemis")
event.container.create_sender(conn, self.address)
def on_connection_error(self, event: Event) -> None:
def on_connection_error(self, event) -> None:
self.log.info(f"error: {event}")
def on_sendable(self, event: Event):
def on_sendable(self, event):
self.log.info("send message")
event.sender.send(Message(body=self.url, address=self.address, content_type="text/json"))
json_content = json.dumps(self.message)
event.sender.send(
Message(body=json_content, address=self.address, content_type="text/json")
)
event.connection.close()
event.sender.close()
@@ -63,19 +79,20 @@ class AddLinkMessage(MessagingHandler):
self.log.info(f"accepted: {event}")
if __name__ == '__main__':
if __name__ == "__main__":
logger = get_logger(args.verbose, args.config)
logger.info('kontor.add_link started')
logger.info("kontor.add_link started")
link: str = args.url
data = {"url": link}
server_url: str = f"amqp://{args.server}:5672"
if args.api:
if args.video:
request: str = "http://127.0.0.1:8800/api/video/files"
else:
request: str = "http://127.0.0.1:8800/api/media/files"
response = requests.post(request, json=data)
logger.info(f"Status: {response.status_code}")
response = requests.post(request, json=data, timeout=5)
logger.info("Status: %s", response.status_code)
data = response.json()
else:
Container(AddLinkMessage("amqp://127.0.0.1:5672", data, logger)).run()
logger.info('kontor.add_link finished')
Container(AddLinkMessage(server=server_url, message=data, log=logger)).run()
logger.info("kontor.add_link finished")
+38
View File
@@ -0,0 +1,38 @@
"""
read file with URLs and store in DB
"""
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
import msgspec
import stomp
from log import get_logger
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-u", "--url", help="link")
parser.add_argument("--config", "-c", default="kontor-docker")
parser.add_argument("--verbose", "-v", action="count", default=0)
parser.add_argument("--server", "-s", default="127.0.0.1")
parser.add_argument("--port", "-p", default="61616")
parser.add_argument("--destination", "-d", default="media.link.add")
args = parser.parse_args()
class Link(msgspec.Struct):
url: str
if __name__ == "__main__":
logger = get_logger(args.verbose, args.config)
logger.info("kontor.add_link_stomp started")
server_url = [(args.server, args.port)]
conn = stomp.Connection(host_and_ports=server_url)
conn.connect(username="artemis", passcode="artemis", wait=True)
link: Link = Link(url=args.url)
json_bytes = msgspec.json.encode(link)
conn.send(body=json_bytes, destination=args.destination)
logger.info("kontor.add_link finished")
+19 -1
View File
@@ -11,7 +11,8 @@ from bs4 import BeautifulSoup
import requests
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from pathlib import Path
from api import Server, get_api_config, get_logger
from api import Server, get_api_config
from log import get_logger
from db.models.media import MediaActor, MediaActorFile, MediaFile
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
@@ -169,6 +170,9 @@ if __name__ == "__main__":
actor_urls: List[str] = get_meta_info(media_file, logger)
if not args.dry_run:
logger.info("add MediaFile %s", media_file)
server.create(logger, "media_file", media_file.export_dict())
else:
logger.info("not adding MediaFile %s", media_file)
for actor_url in actor_urls:
if actor_url in actor_mapping:
media_actor: Optional[MediaActor] = actor_mapping[actor_url]
@@ -183,6 +187,13 @@ if __name__ == "__main__":
logger.info("create mapping with %s", media_actor_file)
if not args.dry_run:
logger.info("add MediaFile Actor mapping %s", media_actor_file)
server.create(
logger, "media_actor_file", media_actor_file.export_dict()
)
else:
logger.info(
"not adding MediaFile Actor mapping %s", media_actor_file
)
else:
actor_name = get_actor_name(actor_url, logger)
if actor_name in actorname_mapping:
@@ -198,6 +209,11 @@ if __name__ == "__main__":
logger.info("update MediaActor with %s", repr(media_actor))
if not args.dry_run:
logger.info("Update MediaActor %s", media_actor)
server.create(
logger, "media_actor", media_actor.export_dict()
)
else:
logger.info("not updating MediaActor %s", media_actor)
media_actor_file = MediaActorFile()
media_actor_file.id = str(uuid.uuid4())
media_actor_file.created_date = datetime.now()
@@ -208,6 +224,8 @@ if __name__ == "__main__":
logger.info("create mapping with %s", media_actor_file)
if not args.dry_run:
logger.info("Add MediaFile Actor mapping")
else:
logger.info("not adding MediaFile Actor mapping")
else:
for media_file in media_files:
logger.info("MediaFile with %s is found", media_file["id"])
-32
View File
@@ -224,38 +224,6 @@ class ApiConfig:
return found_server
def get_logger(level, config: str):
"""
get Logger according to given log level by verbosity.
"""
dirs = PlatformDirs(config)
logging_config = Path(dirs.user_config_dir, "logging-config.yaml")
log_config = None
with open(logging_config, "rt", encoding="utf-8") as f:
log_config = yaml.safe_load(f.read())
logging.config.dictConfig(log_config)
logger = logging.getLogger("development")
if level is not None:
match level:
case 0:
logger.setLevel(logging.CRITICAL)
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
case 1:
logging.getLogger("requests").setLevel(logging.INFO)
logging.getLogger("urllib3").setLevel(logging.INFO)
logger.setLevel(logging.INFO)
case 2:
logger.setLevel(logging.DEBUG)
logging.getLogger("requests").setLevel(logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)
case _:
logger.setLevel(logging.INFO)
logging.getLogger("requests").setLevel(logging.INFO)
logging.getLogger("urllib3").setLevel(logging.INFO)
return logger
def get_api_config(log: Logger, config: str) -> ApiConfig:
"""
Load configuration from file.
+5 -2
View File
@@ -10,8 +10,8 @@ from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from urllib.parse import urlparse
from simple_term_menu import TerminalMenu
from api import Server, get_api_config, get_logger
from api import Server, get_api_config
from log import get_logger
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("--verbose", "-v", action="count", default=0)
@@ -43,6 +43,9 @@ def remove_file(log: Logger, item_data: Dict[str, Any], media_dirs: List[str]):
for file_dir in media_dirs:
log.info("look in %s", file_dir)
file_name = Path(cloud_link).name
if len(file_name) < 5:
log.info("file_name too short, skip deleting")
break
media_file = Path(file_dir, file_name)
if media_file.exists():
log.info("File to remove %s", media_file.absolute())
+52
View File
@@ -0,0 +1,52 @@
import logging.config
def get_logger(level: int, name: str) -> logging.Logger:
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
},
'handlers': {
'console': {
'class': logging.StreamHandler,
'level': logging.DEBUG,
'formatter': 'simple',
'stream': 'ext://sys.stdout'
},
},
'loggers': {
'urllib3.connectionpool': {
'level': 'WARNING',
'propagate': False,
},
'root': {
'level': 'DEBUG',
'handlers': ['console'],
},
},
})
logger = logging.getLogger(name)
if level is not None:
match level:
case 0:
logger.setLevel(logging.CRITICAL)
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
case 1:
logging.getLogger("requests").setLevel(logging.INFO)
logging.getLogger("urllib3").setLevel(logging.INFO)
logging.getLogger("stomp").setLevel(logging.INFO)
logger.setLevel(logging.INFO)
case 2:
logger.setLevel(logging.DEBUG)
logging.getLogger("requests").setLevel(logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)
case _:
logger.setLevel(logging.INFO)
logging.getLogger("requests").setLevel(logging.INFO)
logging.getLogger("urllib3").setLevel(logging.INFO)
return logger
+2 -1
View File
@@ -14,6 +14,7 @@ dependencies = [
"click>=8.1.8",
"coverage>=7.8.0",
"fastapi[standard]>=0.115.12",
"msgspec[toml,yaml]>=0.21.1",
"pathlib>=1.0.1",
"platformdirs>=4.3.7",
"proton>=0.9.1",
@@ -25,5 +26,5 @@ dependencies = [
"simple-term-menu>=1.6.6",
"sqlalchemy>=2.0.40",
"sqlmodel>=0.0.24",
"stomp.py",
"stomp-py",
]
+59 -20
View File
@@ -1,37 +1,76 @@
import stomp
import json
import time
import msgspec
import sys
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from config import get_logger
from typing import Optional
from api import Server, get_api_config
from log import get_logger
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--verbose', '-v', action='count', default=0)
parser.add_argument('--config', '-c', default='kontor-docker')
parser.add_argument("--config", "-c", default="kontor-api")
parser.add_argument("--server", "-s")
parser.add_argument("--messages", "-m", default="127.0.0.1")
parser.add_argument("--port", "-p", default="61616")
args = parser.parse_args()
class MyListener(stomp.ConnectionListener):
def __init__(self, log):
class Link(msgspec.Struct):
url: str
class AddLinkListener(stomp.ConnectionListener):
def __init__(self, log, conn):
self.log = log
self.conn = conn
def on_error(self, frame):
self.log.info(f"received an error {frame.body}")
self.log.info("received an error %s", frame.body)
def on_message(self, frame):
self.log.info(f"received a message '{frame.body}'")
data = json.loads(frame.body)
url = data['url']
self.log.info(f"found link: {url}")
self.log.info("received a message %s", frame.body)
link = msgspec.json.decode(frame.body, type=Link)
self.log.info("found link: %s", link.url)
json_bytes = msgspec.json.encode(link)
self.conn.send(body=json_bytes, destination="add_link_accepted")
self.conn.send(body=json_bytes, destination="update_title")
class UpdateTitleListener(stomp.ConnectionListener):
def __init__(self, log, conn):
self.log = log
self.conn = conn
def on_error(self, frame):
self.log.info("received an error %s", frame.body)
def on_message(self, frame):
self.log.info("received a message %s", frame.body)
link = msgspec.json.decode(frame.body, type=Link)
self.log.info("found link: %s", link.url)
json_bytes = msgspec.json.encode(link)
self.conn.send(body=json_bytes, destination="update_title_accepted")
if __name__ == '__main__':
log = get_logger(args.verbose, args.config)
log.info("kontor.read_queue started")
host = [('127.0.0.1', 61616)]
conn = stomp.Connection(host_and_ports=host)
conn.set_listener('', MyListener(log))
conn.connect(username='artemis', passcode='artemis', wait=True)
conn.subscribe(destination='KontorMediaFile::add_link_file', id=1, ack='auto', headers={})
time.sleep(5)
conn.disconnect()
log.info("kontor.read_queue finished")
logger = get_logger(args.verbose, __file__)
logger.info("kontor.read_queue started")
APICONFIG = get_api_config(logger, args.config)
first_server: Optional[Server] = APICONFIG.get_server("inky")
if not first_server:
sys.exit(2)
data = first_server.request(log=logger, table="media_file")
host = [(args.messages, args.port)]
conn_add = stomp.Connection(host_and_ports=host)
conn_add.set_listener('', AddLinkListener(logger, conn_add))
conn_add.connect(username='artemis', passcode='artemis', wait=True)
conn_add.subscribe(destination='add_link', id=1, ack='auto', headers={})
conn_update = stomp.Connection(host_and_ports=host)
conn_update.set_listener('', UpdateTitleListener(logger, conn_update))
conn_update.connect(username='artemis', passcode='artemis', wait=True)
conn_update.subscribe(destination='update_title', id=1, ack='auto', headers={})
time.sleep(5)
conn_add.disconnect()
conn_update.disconnect()
logger.info("kontor.read_queue finished")
+2 -2
View File
@@ -11,9 +11,9 @@ from api import (
MAPPING,
EndPointNotAvailableException,
Server,
get_api_config,
get_logger,
get_api_config
)
from log import get_logger
parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("--verbose", "-v", action="count", default=0)
+323 -274
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -1,3 +0,0 @@
<factorypath>
<factorypathentry kind="EXTJAR" id="/home/tpeetz/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.30/f195ee86e6c896ea47a1d39defbe20eb59cd149d/lombok-1.18.30.jar" enabled="true" runInBatchMode="false"/>
</factorypath>
+3 -9
View File
@@ -1,9 +1,3 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary
+34 -29
View File
@@ -1,33 +1,38 @@
.gradle/
.settings/
node_modules
HELP.md
.gradle
build/
bin/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
.project
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
.idea/
*.lock
logs/
frontend/generated
frontend/index.html
package*.json
tsconfig.json
types.d.ts
node_modules/
vite.*
kontor*Db
tags*
kontorHSQLDB*
.vs/
.winget
src/main/resources/application-local.properties
src/main/resources/application-prod.properties
src/main/resources/application-*.yml
/uploaded-files/
+3 -3
View File
@@ -1,5 +1,5 @@
# ----------------------------------------------------------------------- #
FROM docker.io/library/gradle:8.7-jdk AS builder
FROM docker.io/library/gradle:9.5-jdk AS builder
WORKDIR /
COPY ./src/main/ ./src/main/
COPY ./frontend/ ./frontend/
@@ -10,11 +10,11 @@ COPY ./gradle/libs.versions.toml ./gradle/
RUN gradle bootJar --no-daemon
# ----------------------------------------------------------------------- #
FROM docker.io/alpine/java:21-jdk AS run
FROM docker.io/alpine/java:25-jdk AS run
RUN mkdir -p /logs
COPY --from=builder /build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar
COPY --from=builder /build/libs/kontor-spring-0.3.0-SNAPSHOT.jar app.jar
EXPOSE 8100
-8
View File
@@ -1,8 +0,0 @@
.PYHONY: all
all:
./gradlew build
docker:
./gradlew dockerImage
-3
View File
@@ -1,3 +0,0 @@
# kontor-spring
Kontor Anwendung mit Spring Boot und Vaadin
+52 -223
View File
@@ -1,261 +1,90 @@
buildscript {
configurations.classpath {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'com.burgstaller' && details.requested.name == 'okhttp-digest' && details.requested.version == '1.10') {
details.useTarget "io.github.rburgst:${details.requested.name}:1.21"
details.because 'Dependency has moved'
}
}
}
repositories {
mavenCentral()
maven { setUrl("https://maven.vaadin.com/vaadin-prereleases") }
maven { setUrl("https://repo.spring.io/milestone") }
}
}
plugins {
id 'java'
id 'application'
id 'java'
id 'maven-publish'
id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.0"
id 'jvm-test-suite'
id 'jacoco'
id 'test-report-aggregation'
id 'jacoco-report-aggregation'
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependencies)
alias(libs.plugins.vaadin)
alias(libs.plugins.lombok)
alias(libs.plugins.asciidoctorPdf)
alias(libs.plugins.asciidoctorConvert)
alias(libs.plugins.asciidoctorGems)
id "de.infolektuell.typst" version "0.8.0"
}
repositories {
mavenCentral()
ruby.gems()
maven { setUrl("https://maven.vaadin.com/vaadin-prereleases") }
maven { setUrl("https://repo.spring.io/milestone") }
maven { setUrl("https://maven.vaadin.com/vaadin-addons") }
alias(libs.plugins.vaadin)
}
java {
sourceCompatibility = JavaVersion.VERSION_17
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
repositories {
maven { setUrl("https://nexus.thpeetz.de/repository/maven-central") }
mavenCentral()
}
ext {
set('vaadinVersion', "25.2.6")
}
dependencies {
implementation 'com.vaadin:vaadin-core'
implementation 'com.vaadin:vaadin-spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-artemis'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.apache.camel.springboot:camel-spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-artemis'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
developmentOnly 'com.vaadin:vaadin-dev'
implementation 'com.vaadin:vaadin-spring-boot-starter'
implementation 'org.apache.camel.springboot:camel-spring-boot-starter:4.22.0'
implementation 'org.apache.camel.springboot:camel-jms-starter'
implementation 'org.apache.activemq:artemis-jakarta-client'
//implementation libs.artemis
implementation 'org.springframework.boot:spring-boot-starter-actuator'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation libs.hypersistence
implementation libs.mail
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.security:spring-security-oauth2-jose'
implementation 'org.springframework.security:spring-security-oauth2-resource-server'
implementation 'com.h2database:h2'
implementation libs.hsqldb
implementation 'org.postgresql:postgresql'
//runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
implementation libs.hypersistence
implementation libs.mail
implementation libs.jackson
implementation libs.gson
implementation libs.json
implementation 'org.hibernate.orm:hibernate-community-dialects'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'com.vaadin:vaadin-testbench-junit5'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.apache.camel:camel-test-spring-junit5'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
asciidoctorGems libs.rouge
//asciidoctorGems libs.diagram
runtimeOnly 'org.postgresql:postgresql'
runtimeOnly 'org.xerial:sqlite-jdbc'
//implementation 'org.hibernate.orm:hibernate-community-dialects'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-artemis-test'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testImplementation 'org.springframework.boot:spring-boot-starter-security-test'
testCompileOnly 'org.projectlombok:lombok'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testAnnotationProcessor 'org.projectlombok:lombok'
}
def pdfFile = layout.buildDirectory.file("docs/asciidocPdf/kontor-spring.pdf")
def pdfArtifact = artifacts.add('archives', pdfFile.get().asFile) {
type 'pdf'
builtBy asciidoctorPdf
dependencyManagement {
imports {
mavenBom libs.vaadin.bom.get().toString()
mavenBom libs.camel.bom.get().toString()
}
}
publishing {
publications {
maven(MavenPublication) {
groupId = group + '.docs'
artifactId = project.name
artifact pdfArtifact
}
bootJava(MavenPublication) {
artifact tasks.named("bootDistTar")
}
artifact tasks.named("bootJar")
}
}
repositories {
maven {
name = "gitlabPackageRegistry"
url = uri("https://gitlab.com/api/v4/projects/64726715/packages/maven")
credentials(PasswordCredentials)
url = version.endsWith('SNAPSHOT') ?
'https://nexus.thpeetz.de/repository/maven-snapshots' :
'https://nexus.thpeetz.de/repository/maven-releases'
credentials {
username = project.findProperty('nexusUser')
password = project.findProperty('nexusPassword')
}
}
}
}
final BUILD_DATE = new Date().format('dd.MM.yyyy').toString()
asciidoctorPdf {
dependsOn asciidoctorGemsPrepare
baseDirFollowsSourceFile()
asciidoctorj {
modules {
diagram.use()
}
requires 'rouge'
attributes 'build-gradle': file('build.gradle'),
'endpoint-url': 'https://www.thpeetz.de',
'source-highlighter': 'rouge',
'imagesdir': './images',
'toc': 'left',
'toc-title': 'Inhaltsverzeichnis',
'revdate': BUILD_DATE,
'revnumber': { project.version.toString() },
'revremark': 'Entwurf',
'chapter-label': '',
'icons': 'font',
'idprefix': 'id_',
'idseparator': '-',
'docinfo1': ''
}
}
build.dependsOn asciidoctorPdf
dependencyManagement {
imports {
mavenBom libs.vaadin.bom.get().toString()
mavenBom libs.camel.bom.get().toString()
}
}
application {
mainClass = 'de.thpeetz.kontor.Application'
}
bootRun {
args = ["--spring.profiles.active=${project.properties['profile'] ?: 'prod'}"]
}
task dockerImage(type: Exec) {
dependsOn(bootJar)
commandLine "docker", "build", ".", "-t", "kontor:${project.version}"
}
vaadin {
productionMode = true
}
testing {
suites {
configureEach {
useJUnitJupiter()
dependencies {
implementation project()
implementation 'com.vaadin:vaadin-core'
implementation 'com.vaadin:vaadin-spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'
implementation libs.hsqldb
implementation libs.sqlite.jdbc
//runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
implementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
implementation 'org.springframework.security:spring-security-test'
implementation 'com.vaadin:vaadin-testbench-junit5'
implementation 'io.projectreactor:reactor-test'
runtimeOnly 'org.junit.platform:junit-platform-launcher'
}
}
test(JvmTestSuite) {
testType = TestSuiteType.UNIT_TEST
targets {
all {
testTask.configure {
reports {
junitXml {
outputPerTestCase = true // defaults to false
mergeReruns = true // defaults to false
}
}
finalizedBy(jacocoTestReport)
}
}
}
}
integrationTest(JvmTestSuite) {
testType = "view-test"
targets {
all {
testTask.configure {
shouldRunAfter(test)
finalizedBy(jacocoTestReport)
}
}
}
}
}
}
tasks.named('check') {
dependsOn(testing.suites.integrationTest)
dependsOn(testing.suites.test)
dependsOn tasks.named('testAggregateTestReport', TestReport)
dependsOn tasks.named('integrationTestAggregateTestReport', TestReport)
}
jacocoTestReport {
dependsOn test, integrationTest
reports {
xml.required = true
csv.required = false
}
}
reporting {
reports {
testAggregateTestReport(AggregateTestReport) {
testType = TestSuiteType.UNIT_TEST
}
integrationTestAggregateTestReport(AggregateTestReport) {
testType = "view-test"
}
integrationTestCodeCoverageReport(JacocoCoverageReport) {
testType = "view-test"
}
}
tasks.named('test') {
useJUnitPlatform()
}
wrapper {
gradleVersion = "8.6"
gradleVersion = libs.versions.gradle.get().toString()
}
@@ -1,3 +0,0 @@
{
"lumoImports" : [ "typography", "color", "spacing", "badge", "utility" ]
}
+4 -1
View File
@@ -1,3 +1,6 @@
description='Kontor with Spring Boot'
version=0.2.0-SNAPSHOT
version=0.3.0-SNAPSHOT
group=de.thpeetz
nexusUser=kontor
nexusPassword=kontorNexus
profile=local
+10 -35
View File
@@ -1,41 +1,31 @@
[versions]
gradle = "8.6"
args4j = "2.33"
commonscli = "1.5.0"
gradle = "9.5.1"
springdependencies = "1.1.7"
springboot = "4.1.0"
vaadin = "25.2.6"
junit = "5.8.2"
logback = "1.1.2"
mockito = "1.9.5"
picoli = "4.7.0"
slf4j = "1.7.22"
hsqldb = "2.7.1"
sqlite = "3.25.2"
spotbugs = "6.0.7"
asciidoctor = "4.0.2"
rouge = "3.15.0"
#diagram = "2.2.2"
diagram = "2.3.1"
sonarqube = "3.3"
cimtConventions = "1.0.0-SNAPSHOT"
springboot = "3.2.5"
springdependencies = "1.1.4"
vaadin = "24.3.8"
camel = "4.10.6"
artemis = "2.41.0"
lombok = "8.6"
lombok = "8.11"
gson = "2.9.0"
jackson = "2.16.1"
json_simple = "1.1.1"
mail = "1.6.2"
hypersistence = "3.9.10"
hypersistence = "3.15.4"
[libraries]
args4j = { module = "args4j:args4j", version.ref = "args4j" }
commonscli = { module = "commons-cli:commons-cli", version.ref = "commonscli" }
vaadin-bom = { group = "com.vaadin", name = "vaadin-bom", version.ref = "vaadin" }
camel-bom = { module = "org.apache.camel.springboot:camel-spring-boot-bom", version.ref = "camel"}
artemis = { module = "org.apache.activemq:artemis-jms-server", version.ref = "artemis" }
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
logbackCore = { module = "ch.qos.logback:logback-core", version.ref = "logback" }
logbackClassic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
mockito = { module = "org.mockito:mockito-all", version.ref = "mockito" }
picocli = { module = "info.picocli:picocli", version.ref = "picoli" }
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
hsqldb = { module = "org.hsqldb:hsqldb", version.ref = "hsqldb" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
@@ -43,15 +33,7 @@ jackson = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref
json = { module = "com.googlecode.json-simple:json-simple", version.ref ="json_simple" }
mail = { module = "com.sun.mail:javax.mail", version.ref ="mail" }
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
hypersistence = { module = "io.hypersistence:hypersistence-utils-hibernate-63", version.ref = "hypersistence" }
vaadin-bom = { module = "com.vaadin:vaadin-bom", version.ref = "vaadin" }
camel-bom = { module = "org.apache.camel.springboot:camel-spring-boot-bom", version.ref = "camel"}
artemis = { module = "org.apache.activemq:artemis-jms-server", version.ref = "artemis" }
asciidoctorGradleJvmGems = { module = "org.asciidoctor:asciidoctor-gradle-jvm-gems", version.ref= "asciidoctor" }
asciidoctorGradleJvm = { module = "org.asciidoctor:asciidoctor-gradle-jvm", version.ref= "asciidoctor" }
asciidoctorGradleJvmPdf = { module = "org.asciidoctor:asciidoctor-gradle-jvm-pdf", version.ref= "asciidoctor" }
rouge = { module = "rubygems:rouge", version.ref = "rouge" }
diagram = { module = "rubygems:asciidoctor-diagram", version.ref = "diagram" }
hypersistence = { module = "io.hypersistence:hypersistence-utils-hibernate-73", version.ref = "hypersistence" }
[bundles]
logback = ["logbackCore", "logbackClassic"]
@@ -59,13 +41,6 @@ logback = ["logbackCore", "logbackClassic"]
[plugins]
spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugs" }
sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" }
asciidoctorPdf = { id = "org.asciidoctor.jvm.pdf", version.ref = "asciidoctor" }
asciidoctorConvert = { id = "org.asciidoctor.jvm.convert", version.ref = "asciidoctor" }
asciidoctorGems = { id = "org.asciidoctor.jvm.gems", version.ref = "asciidoctor" }
javaConvention = { id = "de.cimt.java-conventions", version.ref = "cimtConventions" }
applicationConvention = { id = "de.cimt.application-conventions", version.ref = "cimtConventions" }
libraryConvention = { id = "de.cimt.library-conventions", version.ref = "cimtConventions" }
asciidoctorConvention = { id = "de.cimt.asciidoctor-conventions", version.ref = "cimtConventions" }
spring-boot = { id = "org.springframework.boot", version.ref = "springboot"}
spring-dependencies = { id = "io.spring.dependency-management", version.ref = "springdependencies" }
vaadin = { id = "com.vaadin", version.ref = "vaadin" }
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+7 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -112,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -170,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -203,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
+12 -22
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -21,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -49,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -63,30 +65,18 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
File diff suppressed because it is too large Load Diff
-23
View File
@@ -1,24 +1 @@
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'org.springframework.boot') {
useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
}
if (requested.id.id == 'org.gradle.toolchains.foojay-resolver') {
useModule("org.gradle.toolchains.foojay-resolver-convention:0.4.0")
}
}
}
repositories {
gradlePluginPortal()
mavenCentral()
maven { setUrl("https://maven.vaadin.com/vaadin-prereleases") }
maven { setUrl("https://repo.spring.io/milestone") }
maven { url 'https://plugins.gradle.org/m2/' }
}
// plugins {
// id 'com.vaadin' version "${vaadinVersion}"
// }
}
rootProject.name = 'kontor-spring'
@@ -1,509 +0,0 @@
= Projektbeschreibung kontor-spring: Entwicklungs- und Projekthandbuch
:author: Thomas Peetz
:email: <thomas.peetz@thpeetz.de>
:doctype: book
:sectnums:
:sectnumlevels: 4
:toc:
:toclevels: 4
:table-caption!:
:counter: table-number: 0
[title="Dokumenthistorie", id="Table-{counter:table-number}", options="header"]
|===
| Version | Datum | Autor | Änderungsgrund / Bemerkungen
| 1.0.0 | 16.05.2022 | Thomas Peetz | Ersterstellung
|===
== Allgemeines
=== Zweck des Dokumentes
Das Entwicklungshandbuch beschreibt die Werkzeuge und die Vorgehensweise bei der Entwicklung
im Projekt kontor-spring und der Erstellung der Dokumentation.
=== Verwendete Tools
==== Gitea
Für die Verwaltung des Sourcecode kommt ((Gitea))<<gitea>> zum Einsatz.
Mit Gitea werden auch die Projektaufgaben verwaltet.
Das Projekt und das dazugehörige Git Repository sind unter der Adresse
https://gitea.thpeetz.de/kontor/kontor-spring
zu finden.
== Erstellung der Dokumentation
Die Dokumentation des Projektes wird mit ((Asciidoctor))<<asciidoctor>> geschrieben.
Die Dokumente erhalten ihre Namen nach dem jeweiligen Hauptdokument.
=== Quellcode Verwaltung
Die Asciidoctor-Dateien haben die Endung `.adoc`.
=== Buildsystem
Zur Erstellung der PDF-Dateien aus den Asciidoctor-Dateien wird das Buildsystem ((Gradle))<<gradle>> verwendet.
Die Dateien für die Dokumente liegen im Verzeichnis `src/docs/asciidoc`.
Der Gradle Build wird über die Datei `build.gradle` definiert.
== Einführung
=== Zweck
=== Stakeholder des Systems
=== Systemumfang
==== Zielsetzung des Systems
=== Systemübersicht
==== Systemkontext
==== Systemarchitektur
==== Systemschnittstellen
===== Realisierte Schnittstellen
===== Verwendete Schnittstellen
==== Logisches Datenmodell
===== Benutzer ER-Diagramm
[mermaid, kontor-user-er, png]
.Benutzer ER-Diagramm
....
erDiagram
user {
string id PK
datetime created_date
datetime last_modified_date
int version
string email
boolean enabled
string firstName
string lastName
string password
string token
boolean tokenExpired
string userName UNIQUE
}
role {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
authorization_matrix {
string id PK
datetime created_date
datetime last_modified_date
int version
string user_id FK
string role_id FK
}
module_data {
string id PK
datetime created_date
datetime last_modified_date
int version
boolean import_data
string module_name UNIQUE
}
user ||--o{ authorization_matrix : "matrix"
role ||--o{ authorization_matrix : "matrix"
....
===== Comics ER-Diagramm
[mermaid, kontor-comics-er, png]
.Comics ER-Diagramm
....
erDiagram
comic {
string id PK
datetime created_date
datetime last_modified_date
int version
boolean completed
boolean currentOrder
string title
string publisher_id FK
}
volume {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
string comic_id FK
}
issue {
string id PK
datetime created_date
datetime last_modified_date
int version
boolean in_stock
boolean is_read
string issue_number
string comic_id FK
string volume_id FK
}
publisher {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
artist {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
story_arc {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
string comic_id FK
}
trade_paperback {
string id PK
datetime created_date
datetime last_modified_date
int version
int issueStart
int issueEnd
string name
string comic_id FK
}
worktype {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
comic_work {
string id PK
datetime created_date
datetime last_modified_date
int version
string artist_id FK
string comic_id FK
string worktype_id FK
}
comic ||--o{ comic_work : "1"
artist ||--o{ comic_work : "1"
worktype ||--o{ comic-work : "1"
publisher ||--o{ comic : "1"
comic ||--o{ issue : "1"
comic ||--o{ volume : "1"
comic ||--o{ story_arc : "1"
comic ||--o{ trade_paperback : "1"
volume ||--o{ issue : "1"
....
===== TYSC ER-Diagramm
[mermaid, kontor-tysc-er, png]
.TYSC ER-Diagramm
....
erDiagram
sport {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
team {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
string short_name
string sport_id FK
}
field_position {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
string short_name
string sport_id FK
}
rooster {
string id PK
datetime created_date
datetime last_modified_date
int version
int year
string player_id FK
string position_id FK
string team_id FK
}
player {
string id PK
datetime created_date
datetime last_modified_date
int version
string first_name
string last_name
}
vendor {
string id PK
datetime created_date
datetime last_modified_date
int version
string name
}
card_set {
string id PK
datetime created_date
datetime last_modified_date
int version
boolean insert_set
string name
boolean parallel_set
string vendor_id FK
}
card {
string id PK
datetime created_date
datetime last_modified_date
int version
int cardNumber
int year
string card_set FK
string rooster_id FK
string vendor_id FK
}
sport ||--o{ team : "1"
sport ||--o{ field_position : "1"
field_position ||--o{ rooster : "1"
player ||--o{ rooster : "1"
team ||--o{ rooster : "1"
vendor ||--o{ card : "1"
card_set ||--o{ card : "1"
rooster ||--o{ card : "1"
....
===== Bookshelf ER-Diagramm
[mermaid, kontor-bookshelf-er, png]
.Bookshelf ER-Diagramm
....
erDiagram
article {
string id PK
datetime created_date
datetime last_modified_date
int version
string title
}
book {
string id PK
datetime created_date
datetime last_modified_date
int version
string isbn UNIQUE
string title
int year
string publisher_id FK
}
bookshelf_publisher {
string id PK
datetime created_date
datetime last_modified_date
int version
string name UNIQUE
}
author {
string id PK
datetime created_date
datetime last_modified_date
int version
string first_name
string last_name
}
article_author {
string id PK
datetime created_date
datetime last_modified_date
int version
string article_id FK
string author_id FK
}
book_author {
string id PK
datetime created_date
datetime last_modified_date
int version
string book_id FK
string author_id FK
}
publisher ||--o{ book : "1"
article ||--o{ article_author : "1"
author ||--o{ article_author : "1"
book ||--o{ book_author : "1"
author ||--o{ book_author : "1"
....
===== Mail ER-Diagramm
[mermaid, kontor-mail-er, png]
.Mail ER-Diagramm
....
erDiagram
mail {
string id PK
datetime created_date
datetime last_modified_date
int version
string subject
string content
datetime received_date
datetime sent_date
}
mail_account {
string id PK
datetime created_date
datetime last_modified_date
int version
string host
string password
int port
string protocol
boolean start_tls
string user_name
}
mail_address {
string id PK
datetime created_date
datetime last_modified_date
int version
string internet_address UNIQUE
string personal
string user_id FK
}
user ||--o{ mail_address : "1"
....
==== Einschränkungen
== Anforderungen der Domäne
=== Systemfunktionen
==== Anwendungsfälle
==== Akteure
==== Zielgruppen
=== Anforderungen
==== Anforderungen an externe Schnittstellen
==== Funktionale Anforderungen
==== Qualitätsanforderungen
==== Randbedingungen
==== Weitere Anforderungen
==== Wartungs- und Supportinformationen
=== Verifikation
== Projektbeschreibung
=== Ausgangslage
//==== Rechtliche Vorgaben und Rahmenbedingungen
//=== Rahmenbedingungen
//==== Vorhandene Regelungen
=== Projektziele
=== Projektabgrenzung
//=== Voraussichtliche Kosten
//=== Projektrisiken
//==== Produktivität
//==== Finanzielle Risiken
//==== Akzeptanz
== Projektorganisation
=== Projekt-Aufbauorganisation
=== Rollendefinition
//==== Projektauftraggeber
//==== Projektausschuss
//==== Beratung / Qualitätssicherung
==== Projekteiter
==== Projektteam
==== Liste der Stakeholder
=== Projektablauforganisation
==== Projekt-Phasen
===== Erstellung der Projektdokumentation
== Verschiedenes
=== Erreichbarkeiten
[bibliography]
== Referenzen
- [[[asciidoctor]]] http://asciidoctor.org
- [[[gitea]]] http://www.gitea.org
- [[[gradle]]] http://www.gradle.org
- [[[jenkins]]] http://jenkins-ci.org
[glossary]
== Glossar
[index]
== Index
== Verzeichnisse
=== Abbildungsverzeichnis
=== Tabellenverzeichnis
<<Table-1, Tabelle 1>> <<Table-1>>
@@ -1,41 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Artist;
@SpringBootTest
class ArtistViewTest {
@Autowired
private ArtistView artistView;
@Test
void formShownWhenArtistSelected() {
Grid<Artist> grid = artistView.getGrid();
Artist firstArtist = getFirstItem(grid);
ArtistForm form = artistView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstArtist);
assertTrue(form.isVisible());
assertEquals(firstArtist.getName(), form.name.getValue());
}
private Artist getFirstItem(Grid<Artist> grid) {
int count = grid.getListDataView().getItemCount();
List<Artist> artists = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(5, count);
return artists.get(0);
}
}
@@ -1,63 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.*;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import de.thpeetz.kontor.comics.data.Artist;
@SpringBootTest
class ArtistformTest {
private Artist artist1;
private static final String ARTISTNAME= "Lee, Stan";
@BeforeEach
void setupData() {
artist1 = new Artist();
artist1.setName(ARTISTNAME);
}
@Test
void formFieldsPopulated() {
ArtistForm form = new ArtistForm();
form.setArtist(artist1);
assertEquals(ARTISTNAME, form.name.getValue());
}
@Test
void saveEventHasCorrectValues() {
ArtistForm form = new ArtistForm();
Artist artist = new Artist();
form.setArtist(artist);
form.name.setValue(ARTISTNAME);
AtomicReference<Artist> savedArtistReference = new AtomicReference<>(null);
form.addSaveListener(e -> {
savedArtistReference.set(e.getArtist());
});
form.save.click();
Artist savedArtist = savedArtistReference.get();
assertEquals(ARTISTNAME, savedArtist.getName());
}
@Test
void deleteEventHasCorrectValues() {
ArtistForm form = new ArtistForm();
Artist artist = new Artist();
form.setArtist(artist);
form.name.setValue(ARTISTNAME);
AtomicReference<Artist> deletedArtistReference = new AtomicReference<>(null);
form.addDeleteListener(e -> {
deletedArtistReference.set(e.getArtist());
});
form.delete.click();
Artist deletedArtist = deletedArtistReference.get();
assertEquals(ARTISTNAME, deletedArtist.getName());
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Comic;
@SpringBootTest
public class ComicViewTest {
@Autowired
private ComicView comicView;
@Test
void formShownWhenComicSelected() {
Grid<Comic> grid = comicView.getGrid();
Comic firstComic = getFirstItem(grid);
ComicForm form = comicView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstComic);
assertTrue(form.isVisible());
assertEquals(firstComic.getTitle(), form.title.getValue());
}
private Comic getFirstItem(Grid<Comic> grid) {
int count = grid.getListDataView().getItemCount();
List<Comic> comics = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(169, count);
return comics.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.ComicWork;
@SpringBootTest
class ComicWorkViewTest {
@Autowired
private ComicWorkView comicWorkView;
@Test
void formShownWhenComicSelected() {
Grid<ComicWork> grid = comicWorkView.getGrid();
ComicWork firstComicWork = getFirstItem(grid);
ComicWorkForm form = comicWorkView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstComicWork);
assertTrue(form.isVisible());
assertEquals(firstComicWork.getComic(), form.comic.getValue());
}
private ComicWork getFirstItem(Grid<ComicWork> grid) {
int count = grid.getListDataView().getItemCount();
List<ComicWork> comicWorks = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(18, count);
return comicWorks.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Issue;
@SpringBootTest
public class IssueViewTest {
@Autowired
private IssueView issueView;
@Test
void formShownWhenIssueSelected() {
Grid<Issue> grid = issueView.getGrid();
Issue firstIssue = getFirstItem(grid);
IssueForm form = issueView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstIssue);
assertTrue(form.isVisible());
assertEquals(firstIssue.getIssueNumber(), form.issueNumber.getValue());
}
private Issue getFirstItem(Grid<Issue> grid) {
int count = grid.getListDataView().getItemCount();
List<Issue> issues = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(750, count);
return issues.get(0);
}
}
@@ -1,41 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Publisher;
@SpringBootTest
class PublisherViewTest {
@Autowired
private PublisherView publisherView;
@Test
void formShownWhenPublisherSelected() {
Grid<Publisher> grid = publisherView.getGrid();
Publisher firstPublisher = getFirstItem(grid);
PublisherForm form = publisherView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstPublisher);
assertTrue(form.isVisible());
assertEquals(firstPublisher.getName(), form.name.getValue());
}
private Publisher getFirstItem(Grid<Publisher> grid) {
int count = grid.getListDataView().getItemCount();
List<Publisher> publishers = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(18, count);
return publishers.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.StoryArc;
@SpringBootTest
class StoryArcViewTest {
@Autowired
private StoryArcView storyArcView;
@Test
void formShownWhenStoryArcSelected() {
Grid<StoryArc> grid = storyArcView.getGrid();
StoryArc firstStoryArc = getFirstItem(grid);
StoryArcForm form = storyArcView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstStoryArc);
assertTrue(form.isVisible());
assertEquals(firstStoryArc.getName(), form.name.getValue());
}
private StoryArc getFirstItem(Grid<StoryArc> grid) {
int count = grid.getListDataView().getItemCount();
List<StoryArc> storyArcs = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(3, count);
return storyArcs.get(0);
}
}
@@ -1,46 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.TradePaperback;
@SpringBootTest
class TradePaperbackViewTest {
@Autowired
private TradePaperbackView tradePaperbackView;
@Test
void formShownWhenVolumeSelected() {
Grid<TradePaperback> grid = tradePaperbackView.getGrid();
TradePaperback firstTradePaperback = getFirstItem(grid);
TradePaperBackForm form = tradePaperbackView.getForm();
assertFalse(form.isVisible());
if (firstTradePaperback != null) {
grid.asSingleSelect().setValue(firstTradePaperback);
assertTrue(form.isVisible());
assertEquals(firstTradePaperback.getName(), form.name.getValue());
}
}
private TradePaperback getFirstItem(Grid<TradePaperback> grid) {
int count = grid.getListDataView().getItemCount();
List<TradePaperback> tradePaperbacks = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(40, count);
return tradePaperbacks.get(0);
}
}
@@ -1,50 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Volume;
@SpringBootTest
class VolumeViewTest {
@Autowired
private VolumeView volumeView;
@Test
void formShownWhenVolumeSelected() {
Grid<Volume> grid = volumeView.getGrid();
Volume firstVolume = getFirstItem(grid);
VolumeForm form = volumeView.getForm();
assertFalse(form.isVisible());
if (firstVolume != null) {
grid.asSingleSelect().setValue(firstVolume);
assertTrue(form.isVisible());
assertEquals(firstVolume.getName(), form.name.getValue());
}
}
private Volume getFirstItem(Grid<Volume> grid) {
int count = grid.getListDataView().getItemCount();
List<Volume> volumes = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(0, count);
if (count > 0) {
return volumes.get(0);
} else {
return null;
}
}
}
@@ -1,49 +0,0 @@
package de.thpeetz.kontor.comics.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.comics.data.Worktype;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SpringBootTest
class WorktypeViewTest {
@Autowired
private WorktypeView worktypeView;
@Test
void formShownWhenWorktypeSelected() {
Grid<Worktype> grid = worktypeView.getGrid();
Worktype firstWorktype = getFirstItem(grid);
WorktypeForm form = worktypeView.getForm();
assertFalse(form.isVisible());
if (firstWorktype != null) {
grid.asSingleSelect().setValue(firstWorktype);
assertTrue(form.isVisible());
assertEquals(firstWorktype.getName(), form.name.getValue());
}
}
private Worktype getFirstItem(Grid<Worktype> grid) {
int count = grid.getListDataView().getItemCount();
List<Worktype> worktypes = grid.getListDataView().getItems().collect(Collectors.toList());
log.info("found worktypes: {}", worktypes);
assertEquals(3, count);
return worktypes.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.CardSet;
@SpringBootTest
class CardSetViewTest {
@Autowired
private CardSetView cardSetView;
@Test
void formShownWhenCardSetSelected() {
Grid<CardSet> grid = cardSetView.getGrid();
CardSet firstCardSet = getFirstItem(grid);
CardSetForm form = cardSetView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstCardSet);
assertTrue(form.isVisible());
assertEquals(firstCardSet.getName(), form.name.getValue());
}
private CardSet getFirstItem(Grid<CardSet> grid) {
int count = grid.getListDataView().getItemCount();
List<CardSet> cardSets = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(15, count);
return cardSets.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Card;
@SpringBootTest
class CardViewTest {
@Autowired
private CardView cardView;
@Test
void formShownWhenCardSelected() {
Grid<Card> grid = cardView.getGrid();
Card firstCard = getFirstItem(grid);
CardForm form = cardView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstCard);
assertTrue(form.isVisible());
assertEquals(String.valueOf(firstCard.getCardNumber()), form.cardNumber.getValue());
}
private Card getFirstItem(Grid<Card> grid) {
int count = grid.getListDataView().getItemCount();
List<Card> cards = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(10, count);
return cards.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.FieldPosition;
@SpringBootTest
class FieldPositionViewTest {
@Autowired
private PositionView positionView;
@Test
void formShownWhenPositionSelected() {
Grid<FieldPosition> grid = positionView.getGrid();
FieldPosition firstFieldPosition = getFirstItem(grid);
PositionForm form = positionView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstFieldPosition);
assertTrue(form.isVisible());
assertEquals(firstFieldPosition.getName(), form.name.getValue());
}
private FieldPosition getFirstItem(Grid<FieldPosition> grid) {
int count = grid.getListDataView().getItemCount();
List<FieldPosition> positions = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(44, count);
return positions.get(0);
}
}
@@ -1,44 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Player;
@SpringBootTest
class PlayerViewTest {
@Autowired
private PlayerView playerView;
@Test
void formShownWhenPlayerSelected() {
Grid<Player> grid = playerView.getGrid();
Player firstPlayer = getFirstItem(grid);
PlayerForm form = playerView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstPlayer);
assertTrue(form.isVisible());
assertEquals(firstPlayer.getLastName(), form.lastName.getValue());
assertEquals(firstPlayer.getFirstName(), form.firstName.getValue());
}
private Player getFirstItem(Grid<Player> grid) {
int count = grid.getListDataView().getItemCount();
List<Player> players = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(38, count);
return players.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Rooster;
@SpringBootTest
class RoosterViewTest {
@Autowired
private RoosterView roosterView;
@Test
void formShownWhenRoosterSelected() {
Grid<Rooster> grid = roosterView.getGrid();
Rooster firstRooster = getFirstItem(grid);
RoosterForm form = roosterView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstRooster);
assertTrue(form.isVisible());
assertEquals(firstRooster.getYear(), form.year.getValue());
}
private Rooster getFirstItem(Grid<Rooster> grid) {
int count = grid.getListDataView().getItemCount();
List<Rooster> roosters = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(11, count);
return roosters.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Sport;
@SpringBootTest
class SportViewTest {
@Autowired
private SportView sportView;
@Test
void formShownWhenSportSelected() {
Grid<Sport> grid = sportView.getGrid();
Sport firstSport = getFirstItem(grid);
SportForm form = sportView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstSport);
assertTrue(form.isVisible());
assertEquals(firstSport.getName(), form.name.getValue());
}
private Sport getFirstItem(Grid<Sport> grid) {
int count = grid.getListDataView().getItemCount();
List<Sport> sports = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(4, count);
return sports.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Team;
@SpringBootTest
class TeamViewTest {
@Autowired
private TeamView teamView;
@Test
void formShownWhenTeamSelected() {
Grid<Team> grid = teamView.getGrid();
Team firstTeam = getFirstItem(grid);
TeamForm form = teamView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstTeam);
assertTrue(form.isVisible());
assertEquals(firstTeam.getName(), form.name.getValue());
}
private Team getFirstItem(Grid<Team> grid) {
int count = grid.getListDataView().getItemCount();
List<Team> teams = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(122, count);
return teams.get(0);
}
}
@@ -1,43 +0,0 @@
package de.thpeetz.kontor.tysc.views;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.vaadin.flow.component.grid.Grid;
import de.thpeetz.kontor.tysc.data.Vendor;
@SpringBootTest
class VendorViewTest {
@Autowired
private VendorView vendorView;
@Test
void formShownWhenVendorSelected() {
Grid<Vendor> grid = vendorView.getGrid();
Vendor firstVendor = getFirstItem(grid);
VendorForm form = vendorView.getForm();
assertFalse(form.isVisible());
grid.asSingleSelect().setValue(firstVendor);
assertTrue(form.isVisible());
assertEquals(firstVendor.getName(), form.name.getValue());
}
private Vendor getFirstItem(Grid<Vendor> grid) {
int count = grid.getListDataView().getItemCount();
List<Vendor> vendors = grid.getListDataView().getItems().collect(Collectors.toList());
assertEquals(9, count);
return vendors.get(0);
}
}
@@ -1,30 +0,0 @@
server.port=8085
spring.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
spring.datasource.url=jdbc:hsqldb:mem:itDb
spring.datasource.username=sa
spring.datasource.password=sa
#spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
#spring.datasource.driverClassName=org.sqlite.JDBC
#spring.datasource.url=jdbc:sqlite:file:./kontorITDb?cache=shared
#spring.datasource.username=sa
#spring.datasource.password=sa
spring.jpa.defer-datasource-initialization = true
#spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
spring.sql.init.mode=always
spring.mustache.check-template-location = false
logging.level.org.atmosphere=INFO
logging.level.org.springframework.web=INFO
logging.level.guru.springframework.controllers=DEBUG
logging.level.org.hibernate=INFO
logging.level.de.thpeetz=DEBUG
jwt.auth.secret=J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=
@@ -0,0 +1 @@
export {}
@@ -0,0 +1 @@
export declare const applyCss: (target: Node) => void;
@@ -0,0 +1,707 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/// <reference lib="es2018" />
import { Flow as _Flow } from 'Frontend/generated/jar-resources/Flow.js';
import React, { useCallback, useEffect, useReducer, useRef, useState, type ReactNode } from 'react';
import { matchRoutes, useBlocker, useLocation, useNavigate, type NavigateOptions, useHref } from 'react-router';
import { createPortal } from 'react-dom';
const flow = new _Flow({
imports: () => import('Frontend/generated/flow/generated-flow-imports.js')
});
const router = {
render() {
return Promise.resolve();
}
};
const flowReact : { active: boolean } = {
active: false,
}
// ClickHandler for vaadin-router-go event is copied from vaadin/router click.js
// @ts-ignore
function getAnchorOrigin(anchor) {
// IE11: on HTTP and HTTPS the default port is not included into
// window.location.origin, so won't include it here either.
const port = anchor.port;
const protocol = anchor.protocol;
const defaultHttp = protocol === 'http:' && port === '80';
const defaultHttps = protocol === 'https:' && port === '443';
const host =
defaultHttp || defaultHttps
? anchor.hostname // does not include the port number (e.g. www.example.org)
: anchor.host; // does include the port number (e.g. www.example.org:80)
return `${protocol}//${host}`;
}
function normalizeURL(url: URL): void | string {
// ignore click if baseURI does not match the document (external)
if (!url.href.startsWith(document.baseURI)) {
return;
}
// Normalize path against baseURI
return '/' + url.href.slice(document.baseURI.length);
}
function extractURL(event: MouseEvent): void | URL {
// ignore the click if the default action is prevented
if (event.defaultPrevented) {
return;
}
// ignore the click if not with the primary mouse button
if (event.button !== 0) {
return;
}
// ignore the click if a modifier key is pressed
if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
return;
}
// find the <a> element that the click is at (or within)
let maybeAnchor = event.target;
const path = event.composedPath
? event.composedPath()
: // @ts-ignore
event.path || [];
// example to check: `for...of` loop here throws the "Not yet implemented" error
for (let i = 0; i < path.length; i++) {
const target = path[i];
if (target.nodeName && target.nodeName.toLowerCase() === 'a') {
maybeAnchor = target;
break;
}
}
// @ts-ignore
while (maybeAnchor && maybeAnchor.nodeName.toLowerCase() !== 'a') {
// @ts-ignore
maybeAnchor = maybeAnchor.parentNode;
}
// ignore the click if not at an <a> element
// @ts-ignore
if (!maybeAnchor || maybeAnchor.nodeName.toLowerCase() !== 'a') {
return;
}
const anchor = maybeAnchor as HTMLAnchorElement;
// ignore the click if the <a> element has a non-default target
if (anchor.target && anchor.target.toLowerCase() !== '_self') {
return;
}
// ignore the click if the <a> element has the 'download' attribute
if (anchor.hasAttribute('download')) {
return;
}
// ignore the click if the <a> element has the 'router-ignore' attribute
if (anchor.hasAttribute('router-ignore')) {
return;
}
// ignore the click if the target URL is a fragment on the current page
if (anchor.pathname === window.location.pathname && anchor.hash !== '') {
// @ts-ignore
window.location.hash = anchor.hash;
return;
}
// ignore the click if the target is external to the app
// In IE11 HTMLAnchorElement does not have the `origin` property
// @ts-ignore
const origin = anchor.origin || getAnchorOrigin(anchor);
if (origin !== window.location.origin) {
return;
}
return new URL(anchor.href, anchor.baseURI);
}
function extractPath(event: MouseEvent): void | string {
const url = extractURL(event);
if (!url) {
return;
}
return normalizeURL(url);
}
export const registerGlobalClickHandler = () => {
window.addEventListener('click', (event: MouseEvent) => {
if (flowReact.active) {
return;
}
const url = extractURL(event);
if (!url) {
return;
}
// ignore click if baseURI does not match the document (external)
if (!url.href.startsWith(document.baseURI)) {
return;
}
if (event && event.preventDefault) {
event.preventDefault();
}
// Normalize path against baseURI
const path = url.pathname + url.search + url.hash;
const state = {...window.history.state}
if (state.idx !== undefined) {
state.idx = state.idx + 1;
}
window.history.pushState(state, '', path);
window.dispatchEvent(new PopStateEvent('popstate'));
}, { capture: false });
};
/**
* Fire 'vaadin-navigated' event to inform components of navigation.
* @param pathname pathname of navigation
* @param search search of navigation
*/
function fireNavigated(pathname: string, search: string) {
setTimeout(() => {
window.dispatchEvent(
new CustomEvent('vaadin-navigated', {
detail: {
pathname,
search
}
})
);
// @ts-ignore
delete window.Vaadin.Flow.navigation;
});
}
function postpone() {}
const prevent = () => postpone;
type RouterContainer = Awaited<ReturnType<(typeof flow.serverSideRoutes)[0]['action']>>;
type PortalEntry = {
readonly children: ReactNode;
readonly domNode: HTMLElement;
};
type FlowPortalProps = React.PropsWithChildren<
Readonly<{
domNode: HTMLElement;
onRemove(): void;
}>
>;
function FlowPortal({ children, domNode, onRemove }: FlowPortalProps) {
useEffect(() => {
domNode.addEventListener(
'flow-portal-remove',
(event: Event) => {
event.preventDefault();
onRemove();
},
{ once: true }
);
}, []);
return createPortal(children, domNode);
}
const ADD_FLOW_PORTAL = 'ADD_FLOW_PORTAL';
type AddFlowPortalAction = Readonly<{
type: typeof ADD_FLOW_PORTAL;
portal: React.ReactElement<FlowPortalProps>;
}>;
function addFlowPortal(portal: React.ReactElement<FlowPortalProps>): AddFlowPortalAction {
return {
type: ADD_FLOW_PORTAL,
portal
};
}
const REMOVE_FLOW_PORTAL = 'REMOVE_FLOW_PORTAL';
type RemoveFlowPortalAction = Readonly<{
type: typeof REMOVE_FLOW_PORTAL;
key: string;
}>;
function removeFlowPortal(key: string): RemoveFlowPortalAction {
return {
type: REMOVE_FLOW_PORTAL,
key
};
}
function flowPortalsReducer(
portals: readonly React.ReactElement<FlowPortalProps>[],
action: AddFlowPortalAction | RemoveFlowPortalAction
) {
switch (action.type) {
case ADD_FLOW_PORTAL:
return [...portals, action.portal];
case REMOVE_FLOW_PORTAL:
return portals.filter(({ key }) => key !== action.key);
default:
return portals;
}
}
type NavigateOpts = {
to: string;
callback: boolean;
opts?: NavigateOptions;
};
type NavigateFn = (to: string, callback: boolean, opts?: NavigateOptions) => void;
let navigateInProgress = false;
/**
* A hook providing the `navigate(path: string, opts?: NavigateOptions)` function
* with React Router API that has more consistent history updates. Uses internal
* queue for processing navigate calls.
*/
function useQueuedNavigate(
waitReference: React.MutableRefObject<Promise<void> | undefined>,
navigated: React.MutableRefObject<boolean>
): NavigateFn {
const navigate = useNavigate();
const navigateQueue = useRef<NavigateOpts[]>([]).current;
const [navigateQueueLength, setNavigateQueueLength] = useState(0);
const dequeueNavigation = useCallback(() => {
if (navigateInProgress) {
dequeueNavigationAfterCurrentTask();
return;
}
const navigateArgs = navigateQueue.shift();
if (navigateArgs === undefined) {
// Empty queue, do nothing.
return;
}
const blockingNavigate = async () => {
if (waitReference.current) {
await waitReference.current;
waitReference.current = undefined;
}
navigated.current = !navigateArgs.callback;
navigateInProgress = true;
navigate(navigateArgs.to, navigateArgs.opts);
setNavigateQueueLength(navigateQueue.length);
};
blockingNavigate();
}, [navigate, setNavigateQueueLength]);
const dequeueNavigationAfterCurrentTask = useCallback(() => {
setTimeout(dequeueNavigation, 0);
}, [dequeueNavigation]);
const enqueueNavigation = useCallback(
(to: string, callback: boolean, opts?: NavigateOptions) => {
navigateQueue.push({ to: to, callback: callback, opts: opts });
setNavigateQueueLength(navigateQueue.length);
if (navigateQueue.length === 1) {
// The first navigation can be started right after any pending sync
// jobs, which could add more navigations to the queue.
dequeueNavigationAfterCurrentTask();
}
},
[setNavigateQueueLength, dequeueNavigationAfterCurrentTask]
);
useEffect(
() => () => {
// The Flow component has rendered, but history might not be
// updated yet, as React Router does it asynchronously.
// Use microtask callback for history consistency.
dequeueNavigationAfterCurrentTask();
},
[navigateQueueLength, dequeueNavigationAfterCurrentTask]
);
return enqueueNavigation;
}
const flowNavigation = () => {
// @ts-ignore
window.Vaadin.Flow.navigation = true;
};
function Flow() {
const ref = useRef<HTMLOutputElement>(null);
const navigate = useNavigate();
const blocker = useBlocker(({ currentLocation, nextLocation }) => {
navigated.current =
navigated.current ||
(nextLocation.pathname === currentLocation.pathname &&
nextLocation.search === currentLocation.search &&
nextLocation.hash === currentLocation.hash);
return true;
});
const location = useLocation();
const navigated = useRef<boolean>(false);
const blockerHandled = useRef<boolean>(false);
const fromAnchor = useRef<boolean>(false);
const containerRef = useRef<RouterContainer | undefined>(undefined);
const roundTrip = useRef<Promise<void> | undefined>(undefined);
const queuedNavigate = useQueuedNavigate(roundTrip, navigated);
const basename = useHref('/');
// portalsReducer function is used as state outside the Flow component.
const [portals, dispatchPortalAction] = useReducer(flowPortalsReducer, []);
const addPortalEventHandler = useCallback(
(event: CustomEvent<PortalEntry>) => {
event.preventDefault();
const key = Math.random().toString(36).slice(2);
dispatchPortalAction(
addFlowPortal(
<FlowPortal
key={key}
domNode={event.detail.domNode}
onRemove={() => dispatchPortalAction(removeFlowPortal(key))}
>
{event.detail.children}
</FlowPortal>
)
);
},
[dispatchPortalAction]
);
const navigateEventHandler = useCallback(
(event: MouseEvent) => {
const path = extractPath(event);
if (!path) {
return;
}
if (event && event.preventDefault) {
event.preventDefault();
}
navigated.current = false;
// When navigation is triggered by click on a link, fromAnchor is set to true
// in order to get a server round-trip even when navigating to the same URL again
fromAnchor.current = true;
// @ts-ignore
window.Vaadin.Flow.navigation = true;
navigate(path);
// Dispatch close event for overlay drawer on click navigation.
window.dispatchEvent(new CustomEvent('close-overlay-drawer'));
},
[navigate]
);
const vaadinRouterGoEventHandler = useCallback(
(event: CustomEvent<URL>) => {
const url = event.detail;
const path = normalizeURL(url);
if (!path) {
return;
}
event.preventDefault();
navigate(path);
},
[navigate]
);
const vaadinNavigateEventHandler = useCallback(
(event: CustomEvent<{ state: unknown; url: string; replace?: boolean; callback: boolean }>) => {
// @ts-ignore
window.Vaadin.Flow.navigation = true;
// clean base uri away if for instance redirected to http://localhost/path/user?id=10
// else the whole http... will be appended to the url see #19580
const path = event.detail.url.startsWith(document.baseURI)
? '/' + event.detail.url.slice(document.baseURI.length)
: '/' + event.detail.url;
fromAnchor.current = false;
queuedNavigate(path, event.detail.callback, { state: event.detail.state, replace: event.detail.replace });
},
[navigate]
);
const redirect = useCallback(
(path: string) => {
return () => {
navigate(path, { replace: true });
};
},
[navigate]
);
useEffect(() => {
// @ts-ignore
window.addEventListener('vaadin-router-go', vaadinRouterGoEventHandler);
// @ts-ignore
window.addEventListener('vaadin-navigate', vaadinNavigateEventHandler);
return () => {
// @ts-ignore
window.removeEventListener('vaadin-router-go', vaadinRouterGoEventHandler);
// @ts-ignore
window.removeEventListener('vaadin-navigate', vaadinNavigateEventHandler);
};
}, [vaadinRouterGoEventHandler, vaadinNavigateEventHandler]);
useEffect(() => {
// @ts-ignore
window.addEventListener("popstate", flowNavigation);
window.addEventListener('click', navigateEventHandler);
flowReact.active = true;
return () => {
containerRef.current?.parentNode?.removeChild(containerRef.current);
containerRef.current?.removeEventListener('flow-portal-add', addPortalEventHandler as EventListener);
containerRef.current = undefined;
// @ts-ignore
window.removeEventListener("popstate", flowNavigation);
window.removeEventListener('click', navigateEventHandler);
flowReact.active = false;
};
}, []);
useEffect(() => {
if (blocker.state === 'blocked') {
if (blockerHandled.current) {
// Blocker is handled and the new navigation
// gets queued to be executed after the current handling ends.
const { pathname, state } = blocker.location;
// Clear base name to not get /baseName/basename/path
const pathNoBase = pathname.substring(basename.length);
// path should always start with / else react-router will append to current url
queuedNavigate(pathNoBase.startsWith('/') ? pathNoBase : '/' + pathNoBase, true, {
state: state,
replace: true
});
return;
}
blockerHandled.current = true;
let blockingPromise: any;
roundTrip.current = new Promise<void>(
(resolve, reject) => (blockingPromise = { resolve: resolve, reject: reject })
);
// Release blocker handling after promise is fulfilled
roundTrip.current.then(
() => (blockerHandled.current = false),
() => (blockerHandled.current = false)
);
// Proceed to the blocked location, unless the navigation originates from a click on a link.
// In that case continue with function execution and perform a server round-trip
if (navigated.current && !fromAnchor.current) {
blocker.proceed();
blockingPromise.resolve();
navigateInProgress = false;
return;
}
fromAnchor.current = false;
const { pathname, search } = blocker.location;
const routes = ((window as any)?.Vaadin?.routesConfig || []) as any[];
let matched = matchRoutes(Array.from(routes), pathname);
// Navigation between server routes
// @ts-ignore
if (matched && matched.filter((path) => path.route?.element?.type?.name === Flow.name).length != 0) {
containerRef.current?.onBeforeEnter?.call(
containerRef?.current,
{ pathname, search },
{
prevent() {
blocker.reset();
blockingPromise.resolve();
navigateInProgress = false;
navigated.current = false;
},
redirect,
continue() {
blocker.proceed();
blockingPromise.resolve();
navigateInProgress = false;
}
},
router
);
navigated.current = true;
} else {
// For covering the 'server -> client' use case
Promise.resolve(
containerRef.current?.onBeforeLeave?.call(
containerRef?.current,
{
pathname,
search
},
{ prevent },
router
)
).then((cmd: unknown) => {
if (cmd === postpone && containerRef.current) {
// postponed navigation: expose existing blocker to Flow
containerRef.current.serverConnected = (cancel) => {
if (cancel) {
blocker.reset();
} else {
blocker.proceed();
}
blockingPromise.resolve();
navigateInProgress = false;
};
} else {
// permitted navigation: proceed with the blocker
blocker.proceed();
blockingPromise.resolve();
navigateInProgress = false;
}
});
}
}
}, [blocker.state, blocker.location]);
useEffect(() => {
if (blocker.state === 'blocked') {
return;
}
if (navigated.current) {
navigated.current = false;
fireNavigated(location.pathname, location.search);
return;
}
flow.serverSideRoutes[0]
.action({ pathname: location.pathname, search: location.search })
.then((container) => {
const outlet = ref.current?.parentNode;
if (outlet && outlet !== container.parentNode) {
outlet.append(container);
container.addEventListener('flow-portal-add', addPortalEventHandler as EventListener);
containerRef.current = container;
}
return container.onBeforeEnter?.call(
container,
// Always add base to path as it is cleaned in getFlowRoutePath and will break a route starting with basename
{ pathname: basename + location.pathname, search: location.search },
{
prevent,
redirect,
continue() {
fireNavigated(location.pathname, location.search);
}
},
router
);
})
.then((result: unknown) => {
if (typeof result === 'function') {
result();
}
});
}, [location]);
return (
<>
<output ref={ref} style={{ display: 'none' }} />
{portals}
</>
);
}
Flow.type = 'FlowContainer'; // This is for copilot to recognize this
export const serverSideRoutes = [{ path: '/*', element: <Flow /> }];
/**
* Load the script for an exported WebComponent with the given tag
*
* @param tag name of the exported web-component to load
*
* @returns Promise(resolve, reject) that is fulfilled on script load
*/
export const loadComponentScript = (tag: String): Promise<void> => {
return new Promise((resolve, reject) => {
useEffect(() => {
const script = document.createElement('script');
script.src = `/web-component/${tag}.js`;
script.onload = function () {
resolve();
};
script.onerror = function (err) {
reject(err);
};
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
});
};
interface Properties {
[key: string]: string;
}
/**
* Load WebComponent script and create a React element for the WebComponent.
*
* @param tag custom web-component tag name.
* @param props optional Properties object to create element attributes with
* @param onload optional callback to be called for script onload
* @param onerror optional callback for error loading the script
*/
export const reactElement = (tag: string, props?: Properties, onload?: () => void, onerror?: (err: any) => void) => {
loadComponentScript(tag).then(
() => onload?.(),
(err) => {
if (onerror) {
onerror(err);
} else {
console.error(`Failed to load script for ${tag}.`, err);
}
}
);
if (props) {
return React.createElement(tag, props);
}
return React.createElement(tag);
};
export default Flow;
// @ts-ignore
if (import.meta.hot) {
// @ts-ignore
import.meta.hot.accept((newModule) => {
// A hot module replace for Flow.tsx happens when any JS/TS imported through @JsModule
// or similar is updated because this updates generated-flow-imports.js and that in turn
// is imported by this file. We have no means of hot replacing those files, e.g. some
// custom lit element so we need to reload the page. */
if (newModule) {
window.location.reload();
}
});
}
@@ -0,0 +1,329 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import { createRoot, Root } from 'react-dom/client';
import { createElement, type Dispatch, type ReactElement, type ReactNode, useEffect, useReducer } from 'react';
type FlowStateKeyChangedAction<K extends string, V> = Readonly<{
type: 'stateKeyChanged';
key: K;
value: V;
}>;
type FlowStateReducerAction = FlowStateKeyChangedAction<string, unknown>;
function stateReducer<S extends Readonly<Record<string, unknown>>>(state: S, action: FlowStateReducerAction): S {
switch (action.type) {
case 'stateKeyChanged':
const { value } = action;
return {
...state,
key: value
} as S;
default:
return state;
}
}
type DispatchEvent<T> = T extends undefined ? () => boolean : (value: T) => boolean;
const emptyAction: Dispatch<unknown> = () => {};
/**
* An object with APIs exposed for using in the {@link ReactAdapterElement#render}
* implementation.
*/
export type RenderHooks = {
/**
* A hook API for using stateful JS properties of the Web Component from
* the React `render()`.
*
* @typeParam T - Type of the state value
*
* @param key - Web Component property name, which is used for two-way
* value propagation from the server and back.
* @param initialValue - Fallback initial value (optional). Only applies if
* the Java component constructor does not invoke `setState`.
* @returns A tuple with two values:
* 1. The current state.
* 2. The `set` function for changing the state and triggering render
* @protected
*/
readonly useState: ReactAdapterElement['useState'];
/**
* A hook helper to simplify dispatching a `CustomEvent` on the Web
* Component from React.
*
* @typeParam T - The type for `event.detail` value (optional).
*
* @param type - The `CustomEvent` type string.
* @param options - The settings for the `CustomEvent`.
* @returns The `dispatch` function. The function parameters change
* depending on the `T` generic type:
* - For `undefined` type (default), has no parameters.
* - For other types, has one parameter for the `event.detail` value of that type.
* @protected
*/
readonly useCustomEvent: ReactAdapterElement['useCustomEvent'];
/**
* A hook helper to generate the content element with name attribute to bind
* the server-side Flow element for this component.
*
* This is used together with {@link ReactAdapterComponent::getContentElement}
* to have server-side component attach to the correct client element.
*
* Usage as follows:
*
* const content = hooks.useContent('content');
* return <>
* {content}
* </>;
*
* Note! Not adding the 'content' element into the dom will have the
* server throw a IllegalStateException for element with tag name not found.
*
* @param name - The name attribute of the element
*/
readonly useContent: ReactAdapterElement['useContent'];
};
interface ReadyCallbackFunction {
(): void;
}
/**
* A base class for Web Components that render using React. Enables creating
* adapters for integrating React components with Flow. Intended for use with
* `ReactAdapterComponent` Flow Java class.
*/
export abstract class ReactAdapterElement extends HTMLElement {
#root: Root | undefined = undefined;
#rootRendered: boolean = false;
#rendering: ReactNode | undefined = undefined;
#state: Record<string, unknown> = Object.create(null);
#stateSetters = new Map<string, Dispatch<unknown>>();
#customEvents = new Map<string, DispatchEvent<unknown>>();
#dispatchFlowState: Dispatch<FlowStateReducerAction> = emptyAction;
#readyCallback = new Map<string, ReadyCallbackFunction>();
readonly #renderHooks: RenderHooks;
readonly #Wrapper: () => ReactElement | null;
#unmounting?: Promise<void>;
constructor() {
super();
this.#renderHooks = {
useState: this.useState.bind(this),
useCustomEvent: this.useCustomEvent.bind(this),
useContent: this.useContent.bind(this)
};
this.#Wrapper = this.#renderWrapper.bind(this);
this.#markAsUsed();
}
public async connectedCallback() {
this.#rendering = createElement(this.#Wrapper);
const createNewRoot = this.dispatchEvent(
new CustomEvent('flow-portal-add', {
bubbles: true,
cancelable: true,
composed: true,
detail: {
children: this.#rendering,
domNode: this
}
})
);
if (!createNewRoot || this.#root) {
return;
}
await this.#unmounting;
this.#root = createRoot(this);
this.#maybeRenderRoot();
this.#root.render(this.#rendering);
}
/**
* Add a callback for specified element identifier to be called when
* react element is ready.
* <p>
* For internal use only. May be renamed or removed in a future release.
*
* @param id element identifier that callback is for
* @param readyCallback callback method to be informed on element ready state
* @internal
*/
public addReadyCallback(id: string, readyCallback: ReadyCallbackFunction) {
this.#readyCallback.set(id, readyCallback);
}
public async disconnectedCallback() {
if (!this.#root) {
this.dispatchEvent(
new CustomEvent('flow-portal-remove', {
bubbles: true,
cancelable: true,
composed: true,
detail: {
children: this.#rendering,
domNode: this
}
})
);
} else {
this.#unmounting = Promise.resolve();
await this.#unmounting;
this.#root.unmount();
this.#root = undefined;
}
this.#rootRendered = false;
this.#rendering = undefined;
}
/**
* A hook API for using stateful JS properties of the Web Component from
* the React `render()`.
*
* @typeParam T - Type of the state value
*
* @param key - Web Component property name, which is used for two-way
* value propagation from the server and back.
* @param initialValue - Fallback initial value (optional). Only applies if
* the Java component constructor does not invoke `setState`.
* @returns A tuple with two values:
* 1. The current state.
* 2. The `set` function for changing the state and triggering render
* @protected
*/
protected useState<T>(key: string, initialValue?: T): [value: T, setValue: Dispatch<T>] {
if (this.#stateSetters.has(key)) {
return [this.#state[key] as T, this.#stateSetters.get(key)!];
}
const value = ((this as Record<string, unknown>)[key] as T) ?? initialValue!;
this.#state[key] = value;
Object.defineProperty(this, key, {
enumerable: true,
get(): T {
return this.#state[key];
},
set(nextValue: T) {
this.#state[key] = nextValue;
this.#dispatchFlowState({ type: 'stateKeyChanged', key, value });
}
});
const dispatchChangedEvent = this.useCustomEvent<{ value: T }>(`${key}-changed`, { detail: { value } });
const setValue = (value: T) => {
this.#state[key] = value;
dispatchChangedEvent({ value });
this.#dispatchFlowState({ type: 'stateKeyChanged', key, value });
};
this.#stateSetters.set(key, setValue as Dispatch<unknown>);
return [value, setValue];
}
/**
* A hook helper to simplify dispatching a `CustomEvent` on the Web
* Component from React.
*
* @typeParam T - The type for `event.detail` value (optional).
*
* @param type - The `CustomEvent` type string.
* @param options - The settings for the `CustomEvent`.
* @returns The `dispatch` function. The function parameters change
* depending on the `T` generic type:
* - For `undefined` type (default), has no parameters.
* - For other types, has one parameter for the `event.detail` value of that type.
* @protected
*/
protected useCustomEvent<T = undefined>(type: string, options: CustomEventInit<T> = {}): DispatchEvent<T> {
if (!this.#customEvents.has(type)) {
const dispatch = ((detail?: T) => {
const eventInitDict =
detail === undefined
? options
: {
...options,
detail
};
const event = new CustomEvent(type, eventInitDict);
return this.dispatchEvent(event);
}) as DispatchEvent<T>;
this.#customEvents.set(type, dispatch as DispatchEvent<unknown>);
return dispatch;
}
return this.#customEvents.get(type)! as DispatchEvent<T>;
}
/**
* The Web Component render function. To be implemented by users with React.
*
* @param hooks - the adapter APIs exposed for the implementation.
* @protected
*/
protected abstract render(hooks: RenderHooks): ReactElement | null;
/**
* Prepare content container for Flow to bind server Element to.
*
* @param name container name attribute matching server name attribute
* @protected
*/
protected useContent(name: string): ReactElement | null {
useEffect(() => {
this.#readyCallback.get(name)?.();
}, []);
return createElement('flow-content-container', { name, style: { display: 'contents' } });
}
#maybeRenderRoot() {
if (this.#rootRendered || !this.#root) {
return;
}
this.#root.render(createElement(this.#Wrapper));
this.#rootRendered = true;
}
#renderWrapper(): ReactElement | null {
const [state, dispatchFlowState] = useReducer(stateReducer, this.#state);
this.#state = state;
this.#dispatchFlowState = dispatchFlowState;
return this.render(this.#renderHooks);
}
#markAsUsed(): void {
// @ts-ignore
let vaadinObject = window.Vaadin || {};
// @ts-ignore
if (vaadinObject.developmentMode) {
vaadinObject.registrations = vaadinObject.registrations || [];
vaadinObject.registrations.push({
is: 'ReactAdapterElement',
version: '25.2.6'
});
}
}
}
@@ -0,0 +1 @@
export {}
@@ -0,0 +1,105 @@
import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js';
import '@vaadin/common-frontend/ConnectionIndicator.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/details/src/vaadin-details.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/button/src/vaadin-button.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import '@vaadin/avatar-group/src/vaadin-avatar-group.js';
import '@vaadin/badge/src/vaadin-badge.js';
import '@vaadin/breadcrumbs/src/vaadin-breadcrumbs-item.js';
import '@vaadin/card/src/vaadin-card.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/custom-field/src/vaadin-custom-field.js';
import '@vaadin/date-picker/src/vaadin-date-picker.js';
import 'Frontend/generated/jar-resources/datepickerConnector.js';
import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js';
import '@vaadin/time-picker/src/vaadin-time-picker.js';
import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js';
import 'Frontend/generated/jar-resources/vaadin-time-picker/helpers.js';
import '@vaadin/dialog/src/vaadin-dialog.js';
import 'Frontend/generated/jar-resources/dndConnector.js';
import '@vaadin/form-layout/src/vaadin-form-layout.js';
import '@vaadin/form-layout/src/vaadin-form-item.js';
import '@vaadin/form-layout/src/vaadin-form-row.js';
import '@vaadin/grid/src/vaadin-grid-column-group.js';
import '@vaadin/grid/src/vaadin-grid.js';
import '@vaadin/grid/src/vaadin-grid-column.js';
import '@vaadin/grid/src/vaadin-grid-sorter.js';
import 'Frontend/generated/jar-resources/gridConnector.ts';
import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
import '@vaadin/login/src/vaadin-login-form.js';
import '@vaadin/login/src/vaadin-login-overlay.js';
import '@vaadin/markdown/src/vaadin-markdown.js';
import '@vaadin/master-detail-layout/src/vaadin-master-detail-layout.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/message-input/src/vaadin-message-input.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/popover/src/vaadin-popover.js';
import 'Frontend/generated/jar-resources/vaadin-popover/popover.ts';
import '@vaadin/progress-bar/src/vaadin-progress-bar.js';
import '@vaadin/radio-group/src/vaadin-radio-button.js';
import '@vaadin/radio-group/src/vaadin-radio-group.js';
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
import '@vaadin/select/src/vaadin-select.js';
import 'Frontend/generated/jar-resources/selectConnector.js';
import 'Frontend/generated/jar-resources/tooltip.ts';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/slider/src/vaadin-range-slider.js';
import '@vaadin/slider/src/vaadin-slider.js';
import '@vaadin/split-layout/src/vaadin-split-layout.js';
import '@vaadin/tabs/src/vaadin-tab.js';
import '@vaadin/tabsheet/src/vaadin-tabsheet.js';
import '@vaadin/tabs/src/vaadin-tabs.js';
import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js';
import '@vaadin/email-field/src/vaadin-email-field.js';
import '@vaadin/integer-field/src/vaadin-integer-field.js';
import '@vaadin/number-field/src/vaadin-number-field.js';
import '@vaadin/password-field/src/vaadin-password-field.js';
import '@vaadin/text-area/src/vaadin-text-area.js';
import '@vaadin/text-field/src/vaadin-text-field.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/grid/src/vaadin-grid-tree-toggle.js';
import 'Frontend/generated/jar-resources/treeGridConnector.ts';
import '@vaadin/upload/src/vaadin-upload.js';
import '@vaadin/upload/src/vaadin-upload-button.js';
import '@vaadin/upload/src/vaadin-upload-drop-zone.js';
import '@vaadin/upload/src/vaadin-upload-file-list.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/virtual-list/src/vaadin-virtual-list.js';
import 'Frontend/generated/jar-resources/virtualListConnector.js';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
const loadOnDemand = (key) => { return Promise.resolve(0); }
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
window.Vaadin.Flow.loadOnDemand = loadOnDemand;
window.Vaadin.Flow.resetFocus = () => {
let ae=document.activeElement;
while(ae&&ae.shadowRoot) ae = ae.shadowRoot.activeElement;
return !ae || ae.blur() || ae.focus() || true;
}
@@ -0,0 +1,107 @@
import { injectGlobalWebcomponentCss } from 'Frontend/generated/jar-resources/theme-util.js';
import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js';
import '@vaadin/common-frontend/ConnectionIndicator.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/details/src/vaadin-details.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/button/src/vaadin-button.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import '@vaadin/avatar-group/src/vaadin-avatar-group.js';
import '@vaadin/badge/src/vaadin-badge.js';
import '@vaadin/breadcrumbs/src/vaadin-breadcrumbs-item.js';
import '@vaadin/card/src/vaadin-card.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/custom-field/src/vaadin-custom-field.js';
import '@vaadin/date-picker/src/vaadin-date-picker.js';
import 'Frontend/generated/jar-resources/datepickerConnector.js';
import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js';
import '@vaadin/time-picker/src/vaadin-time-picker.js';
import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js';
import 'Frontend/generated/jar-resources/vaadin-time-picker/helpers.js';
import '@vaadin/dialog/src/vaadin-dialog.js';
import 'Frontend/generated/jar-resources/dndConnector.js';
import '@vaadin/form-layout/src/vaadin-form-layout.js';
import '@vaadin/form-layout/src/vaadin-form-item.js';
import '@vaadin/form-layout/src/vaadin-form-row.js';
import '@vaadin/grid/src/vaadin-grid-column-group.js';
import '@vaadin/grid/src/vaadin-grid.js';
import '@vaadin/grid/src/vaadin-grid-column.js';
import '@vaadin/grid/src/vaadin-grid-sorter.js';
import 'Frontend/generated/jar-resources/gridConnector.ts';
import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
import '@vaadin/login/src/vaadin-login-form.js';
import '@vaadin/login/src/vaadin-login-overlay.js';
import '@vaadin/markdown/src/vaadin-markdown.js';
import '@vaadin/master-detail-layout/src/vaadin-master-detail-layout.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/message-input/src/vaadin-message-input.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/popover/src/vaadin-popover.js';
import 'Frontend/generated/jar-resources/vaadin-popover/popover.ts';
import '@vaadin/progress-bar/src/vaadin-progress-bar.js';
import '@vaadin/radio-group/src/vaadin-radio-button.js';
import '@vaadin/radio-group/src/vaadin-radio-group.js';
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
import '@vaadin/select/src/vaadin-select.js';
import 'Frontend/generated/jar-resources/selectConnector.js';
import 'Frontend/generated/jar-resources/tooltip.ts';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/slider/src/vaadin-range-slider.js';
import '@vaadin/slider/src/vaadin-slider.js';
import '@vaadin/split-layout/src/vaadin-split-layout.js';
import '@vaadin/tabs/src/vaadin-tab.js';
import '@vaadin/tabsheet/src/vaadin-tabsheet.js';
import '@vaadin/tabs/src/vaadin-tabs.js';
import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js';
import '@vaadin/email-field/src/vaadin-email-field.js';
import '@vaadin/integer-field/src/vaadin-integer-field.js';
import '@vaadin/number-field/src/vaadin-number-field.js';
import '@vaadin/password-field/src/vaadin-password-field.js';
import '@vaadin/text-area/src/vaadin-text-area.js';
import '@vaadin/text-field/src/vaadin-text-field.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/grid/src/vaadin-grid-tree-toggle.js';
import 'Frontend/generated/jar-resources/treeGridConnector.ts';
import '@vaadin/upload/src/vaadin-upload.js';
import '@vaadin/upload/src/vaadin-upload-button.js';
import '@vaadin/upload/src/vaadin-upload-drop-zone.js';
import '@vaadin/upload/src/vaadin-upload-file-list.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/virtual-list/src/vaadin-virtual-list.js';
import 'Frontend/generated/jar-resources/virtualListConnector.js';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js';
const loadOnDemand = (key) => { return Promise.resolve(0); }
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
window.Vaadin.Flow.loadOnDemand = loadOnDemand;
window.Vaadin.Flow.resetFocus = () => {
let ae=document.activeElement;
while(ae&&ae.shadowRoot) ae = ae.shadowRoot.activeElement;
return !ae || ae.blur() || ae.focus() || true;
}
@@ -0,0 +1,132 @@
app-shell-imports.d.ts
app-shell-imports.js
css.generated.d.ts
flow/Flow.tsx
flow/ReactAdapter.tsx
flow/generated-flow-imports.d.ts
flow/generated-flow-imports.js
flow/generated-flow-webcomponent-imports.js
index.tsx
jar-resources/Clipboard.d.ts
jar-resources/Clipboard.js
jar-resources/Clipboard.js.map
jar-resources/Download.d.ts
jar-resources/Download.js
jar-resources/Download.js.map
jar-resources/ElementResize.d.ts
jar-resources/ElementResize.js
jar-resources/ElementResize.js.map
jar-resources/Flow.d.ts
jar-resources/Flow.js
jar-resources/Flow.js.map
jar-resources/FlowBootstrap.d.ts
jar-resources/FlowBootstrap.js
jar-resources/FlowClient.d.ts
jar-resources/FlowClient.js
jar-resources/FlowShortcut.js
jar-resources/Fullscreen.d.ts
jar-resources/Fullscreen.js
jar-resources/Fullscreen.js.map
jar-resources/Geolocation.d.ts
jar-resources/Geolocation.js
jar-resources/Geolocation.js.map
jar-resources/PageVisibility.d.ts
jar-resources/PageVisibility.js
jar-resources/PageVisibility.js.map
jar-resources/ReactRouterOutletElement.tsx
jar-resources/ScreenOrientation.d.ts
jar-resources/ScreenOrientation.js
jar-resources/ScreenOrientation.js.map
jar-resources/WakeLock.d.ts
jar-resources/WakeLock.js
jar-resources/WakeLock.js.map
jar-resources/WebShare.d.ts
jar-resources/WebShare.js
jar-resources/WebShare.js.map
jar-resources/comboBoxConnector.js
jar-resources/contextMenuConnector.js
jar-resources/contextMenuTargetConnector.js
jar-resources/copilot-version.js
jar-resources/copilot.d.ts
jar-resources/copilot.js
jar-resources/copilot/base-panel-Fr0D1ZcU.js
jar-resources/copilot/chunk-DiqZc92J.js
jar-resources/copilot/consts-CSALuSsm.js
jar-resources/copilot/copilot-development-setup-user-guide-Db31eO1T.js
jar-resources/copilot/copilot-development-setup-user-guide-utils-DzEVQbWO.js
jar-resources/copilot/copilot-devtools-CYwy4U79.js
jar-resources/copilot/copilot-error-handler-9OpssAH1.js
jar-resources/copilot/copilot-features-plugin-DwQSwtbQ.js
jar-resources/copilot/copilot-feedback-plugin-JMYrBCmQ.js
jar-resources/copilot/copilot-focus-trap-CaZw1c70.js
jar-resources/copilot/copilot-global-vars-later-CWkvR40X.js
jar-resources/copilot/copilot-impersonator-plugin-iN25IekB.js
jar-resources/copilot/copilot-info-plugin-9l6uSELy.js
jar-resources/copilot/copilot-init-step2-tqpZOWcn.js
jar-resources/copilot/copilot-log-plugin-CmwIHcBw.js
jar-resources/copilot/copilot-message-box-CVAh5PSs.js
jar-resources/copilot/copilot-modes-wJyMqHUb.js
jar-resources/copilot/copilot-notification-CCNJdNg4.js
jar-resources/copilot/copilot-notification-UcomqPI8.js
jar-resources/copilot/copilot-server-communicator-impl-B7YDzJpM.js
jar-resources/copilot/copilot-settings-panel-qUN2f6RH.js
jar-resources/copilot/copilot-shortcuts-BzZuUtjW.js
jar-resources/copilot/copilot-stored-machine-state-D6qB_Peh.js
jar-resources/copilot/copilot-tree-impl-DxBvMTRa.js
jar-resources/copilot/copilot-ui-state-Dc6l_5DA.js
jar-resources/copilot/copilot-userinfo-C0s6T_kB.js
jar-resources/copilot/copilot-vaadin-versions-CkxDkDmp.js
jar-resources/copilot/copy-to-clipboard-4Y12mBRr.js
jar-resources/copilot/directive-DWLihZIi.js
jar-resources/copilot/directive-helpers-BTt8P8-5.js
jar-resources/copilot/dom-utils-Cuv93-tQ.js
jar-resources/copilot/early-project-state-LGwavSyI.js
jar-resources/copilot/figma-public/figma-api.d.ts
jar-resources/copilot/icons-CwakCZgK.js
jar-resources/copilot/lit-renderer-fa_B9boC.js
jar-resources/copilot/section-panel-ui-state-hOj_RfX_.js
jar-resources/copilot/shared/copilot-plugin-support.d.ts
jar-resources/copilot/shared/flow-utils.d.ts
jar-resources/copilot/stats-CRkPKCLQ.js
jar-resources/copilot/track-active-mode-event-DkX0nsC6.js
jar-resources/copilot/typescript-BkEBjsia.js
jar-resources/datepickerConnector.js
jar-resources/disableOnClickFunctions.js
jar-resources/dndConnector.js
jar-resources/flow-component-directive.js
jar-resources/flow-component-renderer.js
jar-resources/gridConnector.ts
jar-resources/index.d.ts
jar-resources/index.js
jar-resources/index.js.map
jar-resources/lit-renderer.ts
jar-resources/menubarConnector.js
jar-resources/messageListConnector.js
jar-resources/selectConnector.js
jar-resources/theme-util.js
jar-resources/tooltip.ts
jar-resources/treeGridConnector.ts
jar-resources/vaadin-big-decimal-field.js
jar-resources/vaadin-dev-tools/License.d.ts
jar-resources/vaadin-dev-tools/connection.d.ts
jar-resources/vaadin-dev-tools/hotswap-scroll.d.ts
jar-resources/vaadin-dev-tools/live-reload-connection.d.ts
jar-resources/vaadin-dev-tools/pre-trial-splash-screen.d.ts
jar-resources/vaadin-dev-tools/vaadin-dev-tools.d.ts
jar-resources/vaadin-dev-tools/vaadin-dev-tools.js
jar-resources/vaadin-dev-tools/vaadin-dev-tools.js.map
jar-resources/vaadin-dev-tools/websocket-connection.d.ts
jar-resources/vaadin-grid-flow-selection-column.js
jar-resources/vaadin-popover/popover.ts
jar-resources/vaadin-time-picker/helpers.js
jar-resources/vaadin-time-picker/timepickerConnector.js
jar-resources/vaadin-upload-manager-connector.ts
jar-resources/virtualListConnector.js
jsx-dev-transform/index.ts
jsx-dev-transform/jsx-dev-runtime.ts
jsx-dev-transform/jsx-runtime.ts
layouts.json
routes.tsx
vaadin-featureflags.js
vaadin-react.tsx
vaadin.ts
@@ -0,0 +1,26 @@
/******************************************************************************
* This file is auto-generated by Vaadin.
* If you want to customize the entry point, you can copy this file or create
* your own `index.tsx` in your frontend directory.
* By default, the `index.tsx` file should be in `./frontend/` folder.
*
* NOTE:
* - You need to restart the dev-server after adding the new `index.tsx` file.
* After that, all modifications to `index.tsx` are recompiled automatically.
* - `index.js` is also supported if you don't want to use TypeScript.
******************************************************************************/
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router';
import { router } from 'Frontend/generated/routes.js';
function App() {
return <RouterProvider router={router} />;
}
const outlet = document.getElementById('outlet')!;
let root = (outlet as any)._root ?? createRoot(outlet);
(outlet as any)._root = root;
root.render(createElement(App));
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,199 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* Reads the first item from the system clipboard and returns its text/plain
* and text/html representations. Either field is {@code null} if the
* corresponding MIME type is not present.
*
* The caller is expected to be inside a transient user gesture and to have
* been granted the {@code clipboard-read} permission; otherwise
* {@code navigator.clipboard.read} rejects and this function propagates the
* rejection.
*/
async function readClipboardPayload() {
const items = await navigator.clipboard.read();
if (!items.length) {
return null;
}
const item = items[0];
const get = async (type) => item.types.includes(type) ? (await item.getType(type)).text() : null;
return {
text: await get('text/plain'),
html: await get('text/html')
};
}
/**
* Re-encodes the given {@code <img>} as {@code image/png} via a canvas
* round-trip. The source can be any rasterisable format the browser already
* decodes ({@code image/png}, {@code image/jpeg}, {@code image/svg+xml}, ...);
* the output is always a {@code Promise<Blob>} of {@code image/png}, the only
* image MIME type every browser's asynchronous Clipboard API accepts on write.
*
* Cross-origin images need {@code crossorigin="anonymous"} on the {@code <img>}
* plus matching CORS headers, otherwise the canvas is tainted and
* {@code toBlob} throws.
*/
function imageToPngBlob(img) {
return new Promise((resolve, reject) => {
const draw = () => {
try {
const width = img.naturalWidth || img.width;
const height = img.naturalHeight || img.height;
if (!width || !height) {
reject(new Error('image has no intrinsic size'));
return;
}
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('2D canvas context not available'));
return;
}
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob((png) => (png ? resolve(png) : reject(new Error('canvas.toBlob returned null'))), 'image/png');
}
catch (err) {
reject(err);
}
};
if (img.complete) {
// `complete` is also true for an image that already failed to load or has
// an empty src; those have naturalWidth === 0 and their load/error events
// have already fired and will never fire again, so we must settle here
// rather than wait for an event that never comes.
if (img.naturalWidth > 0) {
draw();
}
else {
reject(new Error('image failed to load or has empty src'));
}
}
else {
img.addEventListener('load', draw, { once: true });
img.addEventListener('error', () => reject(new Error('image load failed')), { once: true });
}
});
}
/**
* Writes any combination of text/plain, text/html and image/png to the system
* clipboard as a single ClipboardItem. Any argument may be {@code null} to omit
* that MIME type; at least one is expected to be non-null (the caller enforces
* this). The image argument is the source {@code <img>}; it is re-encoded as
* {@code image/png} via {@link imageToPngBlob} and the resulting
* {@code Promise<Blob>} is fed directly to {@code ClipboardItem} so the
* {@code navigator.clipboard.write} call stays synchronous inside the user
* gesture (Safari otherwise loses activation on the first await).
*
* The caller is expected to be inside a transient user gesture; otherwise
* {@code navigator.clipboard.write} rejects and this function propagates the
* rejection.
*
* Resolves with the {@code text/plain} value if present, otherwise with the
* {@code text/html} value, otherwise with {@code null} (image-only case).
*/
async function writeClipboardPayload(text, html, image) {
const entries = {};
if (text !== null) {
entries['text/plain'] = text;
}
if (html !== null) {
entries['text/html'] = html;
}
if (image !== null) {
entries['image/png'] = imageToPngBlob(image);
}
await navigator.clipboard.write([new ClipboardItem(entries)]);
return text !== null ? text : html;
}
/**
* Posts each file from a {@code paste} event's {@code clipboardData.files} as
* its own XHR to the URL stored as the named attribute on {@code element}. The
* wire format matches vaadin-upload: raw body, percent-encoded {@code X-Filename}
* header, MIME type in {@code Content-Type}.
*
* Each upload is processed in its own HTTP request, so the UI changes the
* server-side UploadHandler makes through {@code UI.access} are applied to the
* state tree but not sent to the client by the upload response itself. Once
* every upload of the paste has settled this helper dispatches a
* {@code vaadin-paste-upload-finished} event back on {@code element}; a
* server-side listener for that event triggers a normal Flow round trip that
* flushes those pending UI changes — so the API works without {@code @Push},
* exactly like a regular upload completing through the Upload component.
*
* Editable targets ({@code <input>}, {@code <textarea>}, {@code contentEditable})
* are not given any special treatment here: browsers do not paste files into
* those elements, so a paste containing a file in a focused text field is
* still a "the user tried to drop a file on the page" event from the
* application's point of view.
*/
// Monotonic counter incremented once per paste gesture so server-side
// handlers can correlate the parallel fetch POSTs that belong to the same
// paste, and order pastes against each other. Scoped to the browser tab —
// a different tab gets its own counter, but no server-side state crosses
// tabs in this flow.
let pasteSequence = 0;
function uploadPastedFiles(event, element, urlAttribute) {
const files = event.clipboardData?.files;
if (!files || files.length === 0) {
return;
}
const url = element.getAttribute(urlAttribute);
if (!url) {
return;
}
pasteSequence += 1;
const pasteId = String(pasteSequence);
// Surface the file count too: the batch server handler needs it to know
// when the paste has been fully delivered (one fetch per file means the
// server only observes arrivals, not the total).
const fileCount = String(files.length);
const uploads = [];
for (const file of files) {
const headers = {
'X-Filename': encodeURIComponent(file.name),
'X-Paste-Id': pasteId,
'X-Paste-File-Count': fileCount
};
if (file.type) {
headers['Content-Type'] = file.type;
}
// The per-file UploadHandler callback runs as each POST is processed;
// log network/connectivity failures the server will never see otherwise.
uploads.push(fetch(url, { method: 'POST', headers: headers, body: file }).catch((err) => {
console.error('Vaadin clipboard file upload failed', err);
}));
}
// Tell the server the paste's uploads are done so it can flush the queued
// UI updates without requiring @Push. The upload response is written only
// after the handler's UI.access task has applied its changes to the state
// tree, so by the time a fetch settles those changes are guaranteed to be
// picked up by this round trip.
Promise.allSettled(uploads).then(() => {
element.dispatchEvent(new CustomEvent('vaadin-paste-upload-finished'));
});
}
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.clipboard = {
readPayload: readClipboardPayload,
writePayload: writeClipboardPayload,
uploadPastedFiles: uploadPastedFiles
};
export {};
//# sourceMappingURL=Clipboard.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,57 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* Triggers a file download from the given URL using the standard
* <a href download> click pattern.
*
* The anchor is synthesised, clicked synchronously inside the caller's
* gesture context, and removed. The browser then either navigates to the
* URL (server responds with Content-Disposition: attachment) or saves the
* resource directly when the download attribute applies.
*
* The {@code download} attribute is honoured only for same-origin URLs;
* cross-origin responses must set Content-Disposition themselves for the
* filename to take effect.
*/
function startDownload(url, filename) {
const a = document.createElement('a');
a.href = url;
// Always set `download` so the browser saves the response rather than
// navigating to it. Empty value lets the browser pick the filename from
// Content-Disposition or the URL pathname; a non-empty value is the
// suggested filename (honoured only same-origin). Cross-origin responses
// without Content-Disposition: attachment still navigate — that's a
// server-side concern this client helper can't override.
a.download = filename ?? '';
// Opt out of Vaadin's client-side router so the click reaches the
// browser's native download handling instead of being intercepted as an
// in-app navigation. Matches Anchor.setHref(DownloadHandler).
a.setAttribute('router-ignore', '');
// Hidden but in the document — some browsers ignore clicks on detached
// anchors.
a.style.display = 'none';
document.body.appendChild(a);
a.click();
a.remove();
}
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.download = {
start: startDownload
};
export {};
//# sourceMappingURL=Download.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Download.js","sourceRoot":"","sources":["../../../../src/main/frontend/Download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;GAYG;AACH,SAAS,aAAa,CAAC,GAAW,EAAE,QAAiB;IACnD,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;IACb,sEAAsE;IACtE,wEAAwE;IACxE,oEAAoE;IACpE,yEAAyE;IACzE,oEAAoE;IACpE,yDAAyD;IACzD,CAAC,CAAC,QAAQ,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC5B,kEAAkE;IAClE,wEAAwE;IACxE,8DAA8D;IAC9D,CAAC,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IACpC,uEAAuE;IACvE,WAAW;IACX,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,KAAK,EAAE,CAAC;IACV,CAAC,CAAC,MAAM,EAAE,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAa,CAAC;AAC3B,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC;AACnB,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;AACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG;IAC1B,KAAK,EAAE,aAAa;CACrB,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * Triggers a file download from the given URL using the standard\n * <a href download> click pattern.\n *\n * The anchor is synthesised, clicked synchronously inside the caller's\n * gesture context, and removed. The browser then either navigates to the\n * URL (server responds with Content-Disposition: attachment) or saves the\n * resource directly when the download attribute applies.\n *\n * The {@code download} attribute is honoured only for same-origin URLs;\n * cross-origin responses must set Content-Disposition themselves for the\n * filename to take effect.\n */\nfunction startDownload(url: string, filename?: string): void {\n const a = document.createElement('a');\n a.href = url;\n // Always set `download` so the browser saves the response rather than\n // navigating to it. Empty value lets the browser pick the filename from\n // Content-Disposition or the URL pathname; a non-empty value is the\n // suggested filename (honoured only same-origin). Cross-origin responses\n // without Content-Disposition: attachment still navigate — that's a\n // server-side concern this client helper can't override.\n a.download = filename ?? '';\n // Opt out of Vaadin's client-side router so the click reaches the\n // browser's native download handling instead of being intercepted as an\n // in-app navigation. Matches Anchor.setHref(DownloadHandler).\n a.setAttribute('router-ignore', '');\n // Hidden but in the document — some browsers ignore clicks on detached\n // anchors.\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\nconst $wnd = window as any;\n$wnd.Vaadin ??= {};\n$wnd.Vaadin.Flow ??= {};\n$wnd.Vaadin.Flow.download = {\n start: startDownload\n};\n\n// Empty export to ensure TypeScript emits this as an ES module,\n// which is required for Vite to load it via import.\nexport {};\n"]}
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.elementResize = {
/**
* Installs a ResizeObserver on the given element and invokes the callback
* with the rounded content-box width and height each time the element
* resizes. Returns a function that disconnects the observer.
*
* Sub-pixel decimals from contentRect are rounded to integers to avoid
* spamming equal-after-rounding updates back to the server.
*/
observe(element, callback) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
if (!entry.target.isConnected) {
continue;
}
callback({
width: Math.round(entry.contentRect.width),
height: Math.round(entry.contentRect.height)
});
}
});
observer.observe(element);
return () => observer.disconnect();
}
};
export {};
//# sourceMappingURL=ElementResize.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ElementResize.js","sourceRoot":"","sources":["../../../../src/main/frontend/ElementResize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH,MAAM,IAAI,GAAG,MAAa,CAAC;AAC3B,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC;AACnB,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;AACxB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,GAAG;IAC/B;;;;;;;OAOG;IACH,OAAO,CAAC,OAAgB,EAAE,QAA8B;QACtD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;oBAC9B,SAAS;gBACX,CAAC;gBACD,QAAQ,CAAC;oBACP,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC;oBAC1C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;iBAC7C,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;CACF,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * Size data passed to the observe() callback. Field names match the Java\n * Size record so the value can be Jackson-deserialised on the server when\n * forwarded through a trigger-framework input.\n */\ninterface Size {\n width: number;\n height: number;\n}\n\nconst $wnd = window as any;\n$wnd.Vaadin ??= {};\n$wnd.Vaadin.Flow ??= {};\n$wnd.Vaadin.Flow.elementResize = {\n /**\n * Installs a ResizeObserver on the given element and invokes the callback\n * with the rounded content-box width and height each time the element\n * resizes. Returns a function that disconnects the observer.\n *\n * Sub-pixel decimals from contentRect are rounded to integers to avoid\n * spamming equal-after-rounding updates back to the server.\n */\n observe(element: Element, callback: (size: Size) => void): () => void {\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (!entry.target.isConnected) {\n continue;\n }\n callback({\n width: Math.round(entry.contentRect.width),\n height: Math.round(entry.contentRect.height)\n });\n }\n });\n observer.observe(element);\n return () => observer.disconnect();\n }\n};\n\n// Empty export to ensure TypeScript emits this as an ES module,\n// which is required for Vite to load it via import.\nexport {};\n"]}
@@ -0,0 +1,83 @@
import './Clipboard';
import './Download';
import './ElementResize';
import './Geolocation';
import './WakeLock';
export interface FlowConfig {
imports?: () => Promise<any>;
}
interface AppConfig {
productionMode: boolean;
appId: string;
uidl: any;
}
interface AppInitResponse {
appConfig: AppConfig;
pushScript?: string;
}
interface Router {
render: (ctx: NavigationParameters, shouldUpdateHistory: boolean) => Promise<void>;
}
interface HTMLRouterContainer extends HTMLElement {
onBeforeEnter?: (ctx: NavigationParameters, cmd: PreventAndRedirectCommands, router: Router) => void | Promise<any>;
onBeforeLeave?: (ctx: NavigationParameters, cmd: PreventCommands, router: Router) => void | Promise<any>;
serverConnected?: (cancel: boolean, url?: NavigationParameters) => void;
serverPaused?: () => void;
}
interface FlowRoute {
action: (params: NavigationParameters) => Promise<HTMLRouterContainer>;
path: string;
}
export interface NavigationParameters {
pathname: string;
search?: string;
}
export interface PreventCommands {
prevent: () => any;
continue?: () => any;
}
export interface PreventAndRedirectCommands extends PreventCommands {
redirect: (route: string) => any;
}
/**
* Client API for flow UI operations.
*/
export declare class Flow {
config: FlowConfig;
response?: AppInitResponse;
pathname: string;
container: HTMLRouterContainer;
private isActive;
private baseRegex;
private appShellTitle;
private navigation;
constructor(config?: FlowConfig);
/**
* Return a `route` object for vaadin-router in an one-element array.
*
* The `FlowRoute` object `path` property handles any route,
* and the `action` returns the flow container without updating the content,
* delaying the actual Flow server call to the `onBeforeEnter` phase.
*
* This is a specific API for its use with `vaadin-router`.
*/
get serverSideRoutes(): [FlowRoute];
loadingStarted(): void;
loadingFinished(): void;
private get action();
private flowLeave;
private flowNavigate;
private getFlowRoutePath;
private getFlowRouteQuery;
private flowInit;
private loadScript;
private findNonce;
private injectAppIdScript;
private flowInitClient;
private flowInitUi;
private collectBrowserDetails;
private addConnectionIndicator;
private offlineStubAction;
private isFlowClientLoaded;
}
export {};
@@ -0,0 +1,538 @@
import { ConnectionIndicator, ConnectionState } from '@vaadin/common-frontend';
import './Clipboard';
import { currentFullscreenState } from './Fullscreen';
import './Download';
import './ElementResize';
import './Geolocation';
import { currentVisibility } from './PageVisibility';
import { currentScreenOrientationAngle, currentScreenOrientationType } from './ScreenOrientation';
import './WakeLock';
import { isShareSupported } from './WebShare';
class FlowUiInitializationError extends Error {
}
// flow uses body for keeping references
const flowRoot = window.document.body;
const $wnd = window;
const ROOT_NODE_ID = 1; // See StateTree.java
function getClients() {
return Object.keys($wnd.Vaadin.Flow.clients)
.filter((key) => key !== 'TypeScript')
.map((id) => $wnd.Vaadin.Flow.clients[id]);
}
function sendEvent(eventName, data) {
getClients().forEach((client) => client.sendEventMessage(ROOT_NODE_ID, eventName, data));
}
// In the future could be replaced with RegExp.escape()
function escapeRegExp(pattern) {
return pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Client API for flow UI operations.
*/
export class Flow {
config;
response = undefined;
pathname = '';
container;
// flag used to inform Testbench whether a server route is in progress
isActive = false;
baseRegex = /^\//;
appShellTitle;
navigation = '';
constructor(config) {
// Set window.name early so @PreserveOnRefresh can use it to identify the browser tab
// Only set if not already set to preserve any existing value
if (!window.name) {
window.name = `v-${Math.random()}`;
}
flowRoot.$ = flowRoot.$ || [];
this.config = config || {};
// TB checks for the existence of window.Vaadin.Flow in order
// to consider that TB needs to wait for `initFlow()`.
$wnd.Vaadin = $wnd.Vaadin || {};
$wnd.Vaadin.Flow = $wnd.Vaadin.Flow || {};
$wnd.Vaadin.Flow.clients = {
TypeScript: {
isActive: () => this.isActive
}
};
// Set browser details collection function as global for use by refresh()
$wnd.Vaadin.Flow.getBrowserDetailsParameters = this.collectBrowserDetails.bind(this);
// Regular expression used to remove the app-context
const elm = document.head.querySelector('base');
this.baseRegex = new RegExp(`^${
// IE11 does not support document.baseURI
escapeRegExp((document.baseURI || (elm && elm.href) || '/').replace(/^https?:\/\/[^/]+/i, ''))}`);
this.appShellTitle = document.title;
// Put a vaadin-connection-indicator in the dom
this.addConnectionIndicator();
}
/**
* Return a `route` object for vaadin-router in an one-element array.
*
* The `FlowRoute` object `path` property handles any route,
* and the `action` returns the flow container without updating the content,
* delaying the actual Flow server call to the `onBeforeEnter` phase.
*
* This is a specific API for its use with `vaadin-router`.
*/
get serverSideRoutes() {
return [
{
path: '(.*)',
action: this.action
}
];
}
loadingStarted() {
// Make Testbench know that server request is in progress
this.isActive = true;
$wnd.Vaadin.connectionState.loadingStarted();
}
loadingFinished() {
// Make Testbench know that server request has finished
this.isActive = false;
$wnd.Vaadin.connectionState.loadingFinished();
if ($wnd.Vaadin.listener) {
// Listeners registered, do not register again.
return;
}
$wnd.Vaadin.listener = {};
// Listen for click on router-links -> 'link' navigation trigger
// and on <a> nodes -> 'client' navigation trigger.
// Use capture phase to detect prevented / stopped events.
document.addEventListener('click', (_e) => {
if (_e.target) {
if (_e.composedPath().some((node) => node instanceof HTMLElement && node.hasAttribute('router-link'))) {
this.navigation = 'link';
}
else if (_e.composedPath().some((node) => node.nodeName === 'A')) {
this.navigation = 'client';
}
}
}, {
capture: true
});
}
get action() {
// Return a function which is bound to the flow instance, thus we can use
// the syntax `...serverSideRoutes` in vaadin-router.
return async (params) => {
// Store last action pathname so as we can check it in events
this.pathname = params.pathname;
if ($wnd.Vaadin.connectionState.online) {
try {
await this.flowInit();
}
catch (error) {
if (error instanceof FlowUiInitializationError) {
// error initializing Flow: assume connection lost
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
return this.offlineStubAction();
}
else {
throw error;
}
}
}
else {
// insert an offline stub
return this.offlineStubAction();
}
// When an action happens, navigation will be resolved `onBeforeEnter`
this.container.onBeforeEnter = (ctx, cmd) => this.flowNavigate(ctx, cmd);
// For covering the 'server -> client' use case
this.container.onBeforeLeave = (ctx, cmd) => this.flowLeave(ctx, cmd);
return this.container;
};
}
// Send a remote call to `JavaScriptBootstrapUI` to check
// whether navigation has to be cancelled.
async flowLeave(ctx, cmd) {
// server -> server, viewing offline stub, or browser is offline
const { connectionState } = $wnd.Vaadin;
if (this.pathname === ctx.pathname || !this.isFlowClientLoaded() || connectionState.offline) {
return Promise.resolve({});
}
// 'server -> client'
return new Promise((resolve) => {
this.loadingStarted();
// The callback to run from server side to cancel navigation
this.container.serverConnected = (cancel) => {
resolve(cmd && cancel ? cmd.prevent() : cmd?.continue?.());
this.loadingFinished();
};
// Call server side to check whether we can leave the view
sendEvent('ui-leave-navigation', { route: this.getFlowRoutePath(ctx), query: this.getFlowRouteQuery(ctx) });
});
}
// Send the remote call to `UI` to render the flow
// route specified by the context
async flowNavigate(ctx, cmd) {
if (this.response) {
return new Promise((resolve) => {
this.loadingStarted();
// The callback to run from server side once the view is ready
this.container.serverConnected = (cancel, redirectContext) => {
if (cmd && cancel) {
resolve(cmd.prevent());
}
else if (cmd && cmd.redirect && redirectContext) {
resolve(cmd.redirect(redirectContext.pathname));
}
else {
cmd?.continue?.();
this.container.style.display = '';
resolve(this.container);
}
this.loadingFinished();
};
this.container.serverPaused = () => {
this.loadingFinished();
};
// Call server side to navigate to the given route
sendEvent('ui-navigate', {
route: this.getFlowRoutePath(ctx),
query: this.getFlowRouteQuery(ctx),
appShellTitle: this.appShellTitle,
historyState: history.state,
trigger: this.navigation
});
// Default to history navigation trigger.
// Link and client cases are handled by click listener in loadingFinished().
this.navigation = 'history';
});
}
else {
// No server response => offline or erroneous connection
return Promise.resolve(this.container);
}
}
getFlowRoutePath(context) {
// Don't decode the pathname here - let the server handle decoding
// individual path segments. This preserves the distinction between
// literal slashes (path separators) and encoded slashes (%2F, data).
return context.pathname.replace(this.baseRegex, '');
}
getFlowRouteQuery(context) {
return (context.search && context.search.substring(1)) || '';
}
// import flow client modules and initialize UI in server side.
async flowInit() {
// Do not start flow twice
if (!this.isFlowClientLoaded()) {
$wnd.Vaadin.Flow.nonce = this.findNonce();
// show flow progress indicator
this.loadingStarted();
// Initialize server side UI
this.response = await this.flowInitUi();
const { pushScript, appConfig } = this.response;
if (typeof pushScript === 'string') {
await this.loadScript(pushScript);
}
const { appId } = appConfig;
// we use a custom tag for the flow app container
// This must be created before bootstrapMod.init is called as that call
// can handle a UIDL from the server, which relies on the container being available
const tag = `flow-container-${appId.toLowerCase()}`;
const serverCreatedContainer = document.querySelector(tag);
if (serverCreatedContainer) {
this.container = serverCreatedContainer;
}
else {
this.container = document.createElement(tag);
this.container.id = appId;
}
flowRoot.$[appId] = this.container;
// Load bootstrap script with server side parameters
const bootstrapMod = await import('./FlowBootstrap');
bootstrapMod.init(this.response);
// Load custom modules defined by user
if (typeof this.config.imports === 'function') {
this.injectAppIdScript(appId);
await this.config.imports();
}
// Load flow-client module
const clientMod = await import('./FlowClient');
await this.flowInitClient(clientMod);
// hide flow progress indicator
this.loadingFinished();
}
// It might be that components created from server expect that their content has been rendered.
// Appending eagerly the container we avoid these kind of errors.
// Note that the client router will move this container to the outlet if the navigation succeed
if (this.container && !this.container.isConnected) {
this.container.style.display = 'none';
document.body.appendChild(this.container);
}
return this.response;
}
async loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.onload = () => resolve();
script.onerror = reject;
script.src = url;
const { nonce } = $wnd.Vaadin.Flow;
if (nonce !== undefined) {
script.setAttribute('nonce', nonce);
}
document.body.appendChild(script);
});
}
findNonce() {
let nonce;
const scriptTags = document.head.getElementsByTagName('script');
for (const scriptTag of scriptTags) {
if (scriptTag.nonce) {
nonce = scriptTag.nonce;
break;
}
}
return nonce;
}
injectAppIdScript(appId) {
const appIdWithoutHashCode = appId.substring(0, appId.lastIndexOf('-'));
const scriptAppId = document.createElement('script');
scriptAppId.type = 'module';
scriptAppId.setAttribute('data-app-id', appIdWithoutHashCode);
const { nonce } = $wnd.Vaadin.Flow;
if (nonce !== undefined) {
scriptAppId.setAttribute('nonce', nonce);
}
document.body.append(scriptAppId);
}
// After the flow-client javascript module has been loaded, this initializes flow UI
// in the browser.
async flowInitClient(clientMod) {
clientMod.init();
// client init is async, we need to loop until initialized
return new Promise((resolve) => {
const intervalId = setInterval(() => {
// client `isActive() == true` while initializing or processing
const initializing = getClients().reduce((prev, client) => prev || client.isActive(), false);
if (!initializing) {
clearInterval(intervalId);
resolve();
}
}, 5);
});
}
// Returns the `appConfig` object
async flowInitUi() {
// appConfig was sent in the index.html request
const initial = $wnd.Vaadin && $wnd.Vaadin.TypeScript && $wnd.Vaadin.TypeScript.initial;
if (initial) {
$wnd.Vaadin.TypeScript.initial = undefined;
return Promise.resolve(initial);
}
const browserDetails = await this.collectBrowserDetails();
// send a request to the `JavaScriptBootstrapHandler`
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const httpRequest = xhr;
// Browser details are appended as individual query parameters rather
// than as a single JSON-encoded value. A JSON payload in the URL
// produces many percent-encoded escape sequences (%7B, %22, %3A, ...)
// that some firewalls/WAFs (e.g. Sophos) flag and block, which would
// fail the bootstrap on the very first page load. Plain key=value pairs
// avoid that pattern entirely.
const browserDetailsParams = browserDetails
? Object.entries(browserDetails)
.map(([key, value]) => `&${key}=${encodeURIComponent(value)}`)
.join('')
: '';
const requestPath = `?v-r=init&location=${encodeURIComponent(this.getFlowRoutePath(location))}&query=${encodeURIComponent(this.getFlowRouteQuery(location))}${browserDetailsParams}`;
httpRequest.open('GET', requestPath);
httpRequest.onerror = () => reject(new FlowUiInitializationError(`Invalid server response when initializing Flow UI.
${httpRequest.status}
${httpRequest.responseText}`));
httpRequest.onload = () => {
const contentType = httpRequest.getResponseHeader('content-type');
if (contentType && contentType.indexOf('application/json') !== -1) {
resolve(JSON.parse(httpRequest.responseText));
}
else {
httpRequest.onerror();
}
};
httpRequest.send();
});
}
// Collects browser details parameters
async collectBrowserDetails() {
const params = {};
/* Screen height and width */
params['v-sh'] = $wnd.screen.height;
params['v-sw'] = $wnd.screen.width;
/* Browser window dimensions */
params['v-wh'] = $wnd.innerHeight;
params['v-ww'] = $wnd.innerWidth;
/* Body element dimensions */
params['v-bh'] = $wnd.document.body.clientHeight;
params['v-bw'] = $wnd.document.body.clientWidth;
/* Current time */
const date = new Date();
params['v-curdate'] = date.getTime();
/* Current timezone offset (including DST shift) */
const tzo1 = date.getTimezoneOffset();
/* Compare the current tz offset with the first offset from the end
of the year that differs --- if less that, we are in DST, otherwise
we are in normal time */
let dstDiff = 0;
let rawTzo = tzo1;
for (let m = 12; m > 0; m -= 1) {
date.setUTCMonth(m);
const tzo2 = date.getTimezoneOffset();
if (tzo1 !== tzo2) {
dstDiff = tzo1 > tzo2 ? tzo1 - tzo2 : tzo2 - tzo1;
rawTzo = tzo1 > tzo2 ? tzo1 : tzo2;
break;
}
}
/* Time zone offset */
params['v-tzo'] = tzo1;
/* DST difference */
params['v-dstd'] = dstDiff;
/* Time zone offset without DST */
params['v-rtzo'] = rawTzo;
/* DST in effect? */
params['v-dston'] = tzo1 !== rawTzo;
/* Time zone id (if available) */
try {
params['v-tzid'] = Intl.DateTimeFormat().resolvedOptions().timeZone;
}
catch (err) {
params['v-tzid'] = '';
}
/* Window name */
if ($wnd.name) {
params['v-wn'] = $wnd.name;
}
/* Detect touch device support */
let supportsTouch = false;
try {
$wnd.document.createEvent('TouchEvent');
supportsTouch = true;
}
catch (e) {
/* Chrome and IE10 touch detection */
supportsTouch = 'ontouchstart' in $wnd || typeof $wnd.navigator.msMaxTouchPoints !== 'undefined';
}
params['v-td'] = supportsTouch;
/* Device Pixel Ratio */
params['v-pr'] = $wnd.devicePixelRatio;
if ($wnd.navigator.platform) {
params['v-np'] = $wnd.navigator.platform;
}
/* Color scheme from CSS color-scheme property */
const colorScheme = getComputedStyle(document.documentElement).colorScheme.trim();
// "normal" is the default value and means no color scheme is set
params['v-cs'] = colorScheme && colorScheme !== 'normal' ? colorScheme : '';
/* Page visibility — initial state of document.hidden / document.hasFocus() */
params['v-pv'] = currentVisibility();
/* Fullscreen state — initial state of document.fullscreenEnabled / .fullscreenElement */
params['v-fs'] = currentFullscreenState();
/* Screen orientation — initial state of screen.orientation, empty
when the Screen Orientation API is unavailable. */
params['v-so'] = currentScreenOrientationType();
params['v-soa'] = currentScreenOrientationAngle();
/* Theme name - detect which theme is in use */
const computedStyle = getComputedStyle(document.documentElement);
let themeName = '';
if (computedStyle.getPropertyValue('--vaadin-lumo-theme').trim()) {
themeName = 'lumo';
}
else if (computedStyle.getPropertyValue('--vaadin-aura-theme').trim()) {
themeName = 'aura';
}
params['v-tn'] = themeName;
/* Geolocation availability — guarded because tests may reset
window.Vaadin between runs, removing the namespace that
Geolocation.ts installs at import time. */
const geolocation = $wnd.Vaadin.Flow?.geolocation;
if (geolocation) {
params['v-ga'] = await geolocation.queryAvailability();
}
/* Wake-lock availability — same guard rationale as geolocation. */
const wakeLock = $wnd.Vaadin.Flow?.wakeLock;
if (wakeLock) {
params['v-wla'] = wakeLock.queryAvailability();
}
/* Web Share API support */
params['v-ws'] = isShareSupported();
/* Stringify each value (they are parsed on the server side) */
const stringParams = {};
Object.keys(params).forEach((key) => {
const value = params[key];
if (typeof value !== 'undefined') {
stringParams[key] = value.toString();
}
});
return stringParams;
}
// Create shared connection state store and connection indicator
addConnectionIndicator() {
// add connection indicator to DOM
ConnectionIndicator.create();
// Listen to browser online/offline events and update the loading indicator accordingly.
// Note: if flow-client is loaded, it instead handles the state transitions.
$wnd.addEventListener('online', () => {
if (!this.isFlowClientLoaded()) {
// Send an HTTP HEAD request for sw.js to verify server reachability.
// We do not expect sw.js to be cached, so the request goes to the
// server rather than being served from local cache.
// Require network-level failure to revert the state to CONNECTION_LOST
// (HTTP error code is ok since it still verifies server's presence).
$wnd.Vaadin.connectionState.state = ConnectionState.RECONNECTING;
const http = new XMLHttpRequest();
http.open('HEAD', 'sw.js');
http.onload = () => {
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTED;
};
http.onerror = () => {
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
};
// Postpone request to reduce potential net::ERR_INTERNET_DISCONNECTED
// errors that sometimes occurs even if browser says it is online
setTimeout(() => http.send(), 50);
}
});
$wnd.addEventListener('offline', () => {
if (!this.isFlowClientLoaded()) {
$wnd.Vaadin.connectionState.state = ConnectionState.CONNECTION_LOST;
}
});
}
async offlineStubAction() {
const offlineStub = document.createElement('iframe');
const offlineStubPath = './offline-stub.html';
offlineStub.setAttribute('src', offlineStubPath);
offlineStub.setAttribute('style', 'width: 100%; height: 100%; border: 0');
this.response = undefined;
let onlineListener;
const removeOfflineStubAndOnlineListener = () => {
if (onlineListener !== undefined) {
$wnd.Vaadin.connectionState.removeStateChangeListener(onlineListener);
onlineListener = undefined;
}
};
offlineStub.onBeforeEnter = (ctx, _cmds, router) => {
onlineListener = () => {
if ($wnd.Vaadin.connectionState.online) {
removeOfflineStubAndOnlineListener();
router.render(ctx, false);
}
};
$wnd.Vaadin.connectionState.addStateChangeListener(onlineListener);
};
offlineStub.onBeforeLeave = (_ctx, _cmds, _router) => {
removeOfflineStubAndOnlineListener();
};
return offlineStub;
}
isFlowClientLoaded() {
return this.response !== undefined;
}
}
//# sourceMappingURL=Flow.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export const init: (appInitResponse: any) => void;
@@ -0,0 +1,236 @@
/* This is a copy of the regular `BootstrapHandler.js` in the flow-server
module, but with the following modifications:
- The main function is exported as an ES module for lazy initialization.
- Application configuration is passed as a parameter instead of using
replacement placeholders as in the regular bootstrapping.
- It reuses `Vaadin.Flow.clients` if exists.
- Fixed lint errors.
*/
const init = function (appInitResponse) {
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
var apps = {};
var widgetsets = {};
var log;
if (typeof window.console === undefined || !window.location.search.match(/[&?]debug(&|$)/)) {
/* If no console.log present, just use a no-op */
log = function () {};
} else if (typeof window.console.log === 'function') {
/* If it's a function, use it with apply */
log = function () {
window.console.log.apply(window.console, arguments);
};
} else {
/* In IE, its a native function for which apply is not defined, but it works
without a proper 'this' reference */
log = window.console.log;
}
var isInitializedInDom = function (appId) {
var appDiv = document.getElementById(appId);
if (!appDiv) {
return false;
}
for (var i = 0; i < appDiv.childElementCount; i++) {
var className = appDiv.childNodes[i].className;
/* If the app div contains a child with the class
'v-app-loading' we have only received the HTML
but not yet started the widget set
(UIConnector removes the v-app-loading div). */
if (className && className.indexOf('v-app-loading') != -1) {
return false;
}
}
return true;
};
/*
* Needed for Testbench compatibility, but prevents any Vaadin 7 app from
* bootstrapping unless the legacy vaadinBootstrap.js file is loaded before
* this script.
*/
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
/**
* Triggers a CSS animation on an element by adding a class, then
* removes the class when the animation ends.
*/
window.Vaadin.Flow.flashClass = function (element, className) {
element.classList.remove(className);
void element.offsetWidth;
element.classList.add(className);
function onAnimationEnd(e) {
if (e.target === element) {
element.classList.remove(className);
element.removeEventListener('animationend', onAnimationEnd);
}
}
element.addEventListener('animationend', onAnimationEnd);
requestAnimationFrame(function () {
var style = getComputedStyle(element);
var animName = style.animationName;
if (!animName || animName === 'none') {
element.classList.remove(className);
element.removeEventListener('animationend', onAnimationEnd);
}
});
};
/**
* Needed for wrapping custom javascript functionality in the components (i.e. connectors)
*/
window.Vaadin.Flow.tryCatchWrapper = function (originalFunction, component) {
return function () {
try {
// eslint-disable-next-line
const result = originalFunction.apply(this, arguments);
return result;
} catch (error) {
console.error(
`There seems to be an error in ${component}:
${error.message}
Please submit an issue to https://github.com/vaadin/flow-components/issues/new/choose`
);
}
};
};
if (!window.Vaadin.Flow.initApplication) {
window.Vaadin.Flow.clients = window.Vaadin.Flow.clients || {};
/**
* Initializes a Flow application with the given ID and configuration,
* and triggers the widgetset callback to start the client engine.
*/
window.Vaadin.Flow.initApplication = function (appId, config) {
var testbenchId = appId.replace(/-\d+$/, '');
if (apps[appId]) {
if (
window.Vaadin &&
window.Vaadin.Flow &&
window.Vaadin.Flow.clients &&
window.Vaadin.Flow.clients[testbenchId] &&
window.Vaadin.Flow.clients[testbenchId].initializing
) {
throw new Error('Application ' + appId + ' is already being initialized');
}
if (isInitializedInDom(appId)) {
if (appInitResponse.appConfig.productionMode) {
throw new Error('Application ' + appId + ' already initialized');
}
// Remove old contents for Flow
var appDiv = document.getElementById(appId);
for (var i = 0; i < appDiv.childElementCount; i++) {
appDiv.childNodes[i].remove();
}
// For devMode reset app config and restart widgetset as client
// is up and running after hrm update.
const getConfig = function (name) {
return config[name];
};
/* Export public data */
const app = {
getConfig: getConfig
};
apps[appId] = app;
if (widgetsets['client'].callback) {
log('Starting from bootstrap', appId);
widgetsets['client'].callback(appId);
} else {
log('Setting pending startup', appId);
widgetsets['client'].pendingApps.push(appId);
}
return apps[appId];
}
}
log('init application', appId, config);
window.Vaadin.Flow.clients[testbenchId] = {
isActive: function () {
return true;
},
initializing: true,
productionMode: mode
};
var getConfig = function (name) {
var value = config[name];
return value;
};
/* Export public data */
var app = {
getConfig: getConfig
};
apps[appId] = app;
var widgetset = 'client';
widgetsets[widgetset] = {
pendingApps: []
};
if (widgetsets[widgetset].callback) {
log('Starting from bootstrap', appId);
widgetsets[widgetset].callback(appId);
} else {
log('Setting pending startup', appId);
widgetsets[widgetset].pendingApps.push(appId);
}
return app;
};
/** Returns an array of all registered application IDs */
window.Vaadin.Flow.getAppIds = function () {
var ids = [];
for (var id in apps) {
if (Object.prototype.hasOwnProperty.call(apps, id)) {
ids.push(id);
}
}
return ids;
};
/** Returns the application object for the given ID */
window.Vaadin.Flow.getApp = function (appId) {
return apps[appId];
};
/**
* Registers a widgetset callback and starts any applications
* that are waiting for it.
*/
window.Vaadin.Flow.registerWidgetset = function (widgetset, callback) {
log('Widgetset registered', widgetset);
var ws = widgetsets[widgetset];
if (ws && ws.pendingApps) {
ws.callback = callback;
for (var i = 0; i < ws.pendingApps.length; i++) {
var appId = ws.pendingApps[i];
log('Starting from register widgetset', appId);
callback(appId);
}
ws.pendingApps = null;
}
};
}
log('Flow bootstrap loaded');
if (appInitResponse.appConfig.productionMode && typeof window.__gwtStatsEvent != 'function') {
window.Vaadin.Flow.gwtStatsEvents = [];
window.__gwtStatsEvent = function (event) {
window.Vaadin.Flow.gwtStatsEvents.push(event);
return true;
};
}
var config = appInitResponse.appConfig;
var mode = appInitResponse.appConfig.productionMode;
window.Vaadin.Flow.initApplication(config.appId, config);
};
export { init };
@@ -0,0 +1 @@
export const init: () => void;
File diff suppressed because one or more lines are too long
@@ -0,0 +1,123 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/*
* Client-side helpers for keyboard shortcuts. Loaded on demand by
* ShortcutRegistration (see initShortcutClient) the same way FlowWebPush.js is
* loaded by WebPush. Provides the popover/modal origin guards (#24974) and the
* keydown delegate used when a shortcut listens on a browser-only element.
*/
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
window.Vaadin.Flow.shortcut = window.Vaadin.Flow.shortcut || {
// Nearest open popover/modal ancestor of the given node in the flattened
// (composed) tree, so slotted light-DOM content resolves to the overlay in a
// component's shadow root.
_scopeOf: function (node) {
while (node) {
if (node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
return node;
}
node = node.assignedSlot || node.parentNode || node.host || null;
}
return null;
},
// Nearest open popover/modal ancestor of the event target.
_eventScope: function (event) {
const path = event.composedPath();
for (let i = 0; i < path.length; i++) {
const node = path[i];
if (node && node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
return node;
}
}
return null;
},
// Delegate path: suppress when an open popover/modal sits between the event
// target and the boundary element the listener is attached to. Returns true
// when the shortcut is allowed to fire. Fails open on error.
eventWithinBoundary: function (event, boundary) {
try {
const path = event.composedPath();
const boundaryIndex = path.indexOf(boundary);
if (boundaryIndex < 0) {
return true;
}
for (let i = 0; i < boundaryIndex; i++) {
const node = path[i];
if (node && node.nodeType === 1 && node.matches && (node.matches(':popover-open') || node.matches(':modal'))) {
return false;
}
}
return true;
} catch (e) {
return true;
}
},
// Normal path: fire only when the event and the shortcut owner (located via
// the given attribute selector) share the same popover/modal scope. Returns
// true when the shortcut is allowed to fire. Fails open on error.
//
// A relayed clone (see registerKeydownDelegate) carries the real origin scope
// in _vaadinShortcutOriginScope, because its own composedPath points at the
// listenOn element and no longer reflects where the keydown happened.
eventInOwnerScope: function (event, ownerSelector) {
try {
const owner = document.querySelector(ownerSelector);
if (!owner) {
return true;
}
const eventScope =
'_vaadinShortcutOriginScope' in event
? event._vaadinShortcutOriginScope
: window.Vaadin.Flow.shortcut._eventScope(event);
return eventScope === window.Vaadin.Flow.shortcut._scopeOf(owner);
} catch (e) {
return true;
}
},
// Relays keydown events from a browser-only element (found by the JS locator)
// to the listenOn component. When the given matcher accepts the event a clone
// is re-dispatched to listenOn so the server-side shortcut listener fires.
// (Previously the inline ELEMENT_LOCATOR_JS in ShortcutRegistration.)
registerKeydownDelegate: function (listenOn, delegate, matches, resetFocus, allowDefault) {
if (!delegate) {
throw 'Shortcut listenOn element not found with the given JS locator';
}
delegate.addEventListener('keydown', function (event) {
if (matches(event, delegate)) {
if (resetFocus) {
window.Vaadin.Flow.resetFocus();
}
const clone = new event.constructor(event.type, event);
// Remember where the keydown actually originated: the clone is
// re-targeted at listenOn, so its composedPath can no longer tell a
// downstream owner-scope guard that the event came from this overlay.
clone._vaadinShortcutOriginScope = window.Vaadin.Flow.shortcut._eventScope(event);
listenOn.dispatchEvent(clone);
if (!allowDefault) {
event.preventDefault();
}
event.stopPropagation();
}
});
}
};
@@ -0,0 +1,7 @@
type VaadinFullscreenState = 'UNSUPPORTED' | 'NOT_FULLSCREEN' | 'FULLSCREEN';
/**
* Returns the current fullscreen state synchronously. Used by the bootstrap
* path to seed the server-side signal without waiting for a DOM event.
*/
export declare function currentFullscreenState(): VaadinFullscreenState;
export {};
@@ -0,0 +1,127 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* Returns the current fullscreen state synchronously. Used by the bootstrap
* path to seed the server-side signal without waiting for a DOM event.
*/
export function currentFullscreenState() {
if (document.fullscreenEnabled !== true) {
return 'UNSUPPORTED';
}
return document.fullscreenElement ? 'FULLSCREEN' : 'NOT_FULLSCREEN';
}
// Dispatch on document.body so the server-side Page facade (listening on
// the UI element, which is body) can update its signal.
function dispatch(state) {
document.body.dispatchEvent(new CustomEvent('vaadin-fullscreen-change', { detail: state }));
}
// Tracks the most recent component-fullscreen setup so the wrapper can be
// torn down when fullscreen exits (programmatically or via Escape) or when
// a new fullscreen request supersedes it.
let activeComponentReset;
function resetComponentIfActive() {
if (activeComponentReset) {
const fn = activeComponentReset;
activeComponentReset = undefined;
fn();
}
}
document.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
resetComponentIfActive();
}
dispatch(currentFullscreenState());
});
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.fullscreen = {
/**
* Requests fullscreen for the entire page (document.documentElement).
* Resolves once the browser has entered fullscreen; rejects with the
* browser's error if the request is refused (no user activation,
* permissions policy, etc.) or with a custom error if fullscreen is not
* supported.
*/
async requestPageFullscreen() {
resetComponentIfActive();
if (document.fullscreenEnabled !== true) {
throw new Error('Fullscreen is not supported');
}
await document.documentElement.requestFullscreen();
},
/**
* Requests fullscreen for a specific component by moving it into the
* given wrapper element and hiding the rest of the view. Fullscreens
* document.documentElement so that Vaadin theming and overlay
* components keep working. The component is restored to its original
* position on exit (programmatic, Escape, or a superseding request).
* If the browser rejects the request, the DOM is rolled back before the
* promise rejects with the browser's error.
*/
async requestComponentFullscreen(element, wrapper) {
resetComponentIfActive();
if (document.fullscreenEnabled !== true) {
throw new Error('Fullscreen is not supported');
}
const originalParent = element.parentNode;
if (!originalParent) {
throw new Error('Component is not attached to the DOM');
}
// The view root is the wrapper's current element child (the route
// content). Capture it before touching the DOM, because the steps below
// insert a placeholder comment and move the element into the wrapper —
// after that, the wrapper's first node may be the placeholder rather than
// the view root. Use firstElementChild so comment/text nodes are skipped.
const viewRoot = wrapper.firstElementChild;
const placeholder = document.createComment('vaadin-fullscreen-placeholder');
originalParent.insertBefore(placeholder, element);
wrapper.appendChild(element);
// When the fullscreened component *is* the view root there is nothing
// else to hide; hiding it would blank the fullscreen. Otherwise hide the
// view root so only the fullscreened component shows.
const hidden = viewRoot === element ? null : viewRoot;
const previousDisplay = hidden?.style.display ?? '';
if (hidden) {
hidden.style.display = 'none';
}
activeComponentReset = () => {
placeholder.parentNode?.insertBefore(element, placeholder);
placeholder.remove();
if (hidden) {
hidden.style.display = previousDisplay;
}
};
try {
await document.documentElement.requestFullscreen();
}
catch (e) {
// Browser rejected the request — undo the DOM changes so the page
// does not end up looking fullscreened without actually being so.
resetComponentIfActive();
throw e;
}
},
/**
* Exits fullscreen mode if the page is currently in fullscreen.
*/
exitFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
}
}
};
//# sourceMappingURL=Fullscreen.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,154 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
function copyCoords(c) {
return {
latitude: c.latitude,
longitude: c.longitude,
accuracy: c.accuracy,
altitude: c.altitude,
altitudeAccuracy: c.altitudeAccuracy,
heading: c.heading,
speed: c.speed
};
}
const watches = new Map();
// The cached availability for the current page. Populated on first
// queryAvailability() call, refreshed from each get()/watch() outcome, and
// kept current by a permissionchange listener (where supported).
let cachedAvailability = null;
let permissionChangeListenerInstalled = false;
function publishAvailability(next) {
if (cachedAvailability === next) {
return;
}
cachedAvailability = next;
// Dispatch on document.body so the server-side Geolocation facade (listening
// on the UI element, which is body) can update its cached value.
document.body.dispatchEvent(new CustomEvent('vaadin-geolocation-availability-change', {
detail: { availability: next }
}));
}
// Applies a single get()/watch() outcome to the cached availability and
// returns the value to report in the response. Never overwrites
// UNSUPPORTED, which is session-stable. TIMEOUT and POSITION_UNAVAILABLE
// don't reveal the permission state, so the previous cached value is
// returned unchanged.
function getAndCacheAvailabilityFromResult(position, error) {
if (cachedAvailability !== 'UNSUPPORTED') {
if (position) {
publishAvailability('GRANTED');
}
else if (error?.code === 1) {
publishAvailability('DENIED');
}
}
return cachedAvailability ?? 'UNKNOWN';
}
async function resolveAvailability() {
if (!window.isSecureContext) {
return 'UNSUPPORTED';
}
// Chromium exposes document.featurePolicy; Firefox and Safari do not
// expose any feature-policy introspection API, so the check is only
// possible on Chromium. When absent, assume geolocation is allowed.
const doc = document;
if (doc.featurePolicy && typeof doc.featurePolicy.allowsFeature === 'function') {
try {
if (!doc.featurePolicy.allowsFeature('geolocation')) {
return 'UNSUPPORTED';
}
}
catch (_e) {
// Ignore and assume allowed
}
}
try {
const status = await navigator.permissions.query({ name: 'geolocation' });
if (!permissionChangeListenerInstalled) {
permissionChangeListenerInstalled = true;
status.addEventListener('change', () => {
publishAvailability(stateToAvailability(status.state));
});
}
return stateToAvailability(status.state);
}
catch (_e) {
// Safari rejects the 'geolocation' permission name with a TypeError
return 'UNKNOWN';
}
}
function stateToAvailability(state) {
switch (state) {
case 'granted':
return 'GRANTED';
case 'denied':
return 'DENIED';
case 'prompt':
return 'PROMPT';
default:
return 'UNKNOWN';
}
}
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.geolocation = {
get(options) {
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((p) => {
const position = { coords: copyCoords(p.coords), timestamp: p.timestamp };
resolve({ position, availability: getAndCacheAvailabilityFromResult(position, undefined) });
}, (e) => {
const error = { code: e.code, message: e.message };
resolve({ error, availability: getAndCacheAvailabilityFromResult(undefined, error) });
}, options || undefined);
});
},
watch(element, options, watchKey) {
if (watches.has(watchKey)) {
navigator.geolocation.clearWatch(watches.get(watchKey));
}
watches.set(watchKey, navigator.geolocation.watchPosition((p) => {
const position = { coords: copyCoords(p.coords), timestamp: p.timestamp };
getAndCacheAvailabilityFromResult(position, undefined);
element.dispatchEvent(new CustomEvent('vaadin-geolocation-position', {
detail: position
}));
}, (e) => {
const error = { code: e.code, message: e.message };
getAndCacheAvailabilityFromResult(undefined, error);
element.dispatchEvent(new CustomEvent('vaadin-geolocation-error', {
detail: error
}));
}, options || undefined));
},
clearWatch(watchKey) {
if (watches.has(watchKey)) {
navigator.geolocation.clearWatch(watches.get(watchKey));
watches.delete(watchKey);
}
},
async queryAvailability() {
const value = await resolveAvailability();
// publish without dispatching a change event — there is no previous
// cached value to compare against when cachedAvailability is null and
// the bootstrap consumer just wants the initial answer.
cachedAvailability = value;
return value;
}
};
export {};
//# sourceMappingURL=Geolocation.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
type VaadinPageVisibility = 'VISIBLE' | 'VISIBLE_NOT_FOCUSED' | 'HIDDEN';
/**
* Returns the current visibility state synchronously. Used by the bootstrap
* path to seed the server-side signal without waiting for a DOM event.
*/
export declare function currentVisibility(): VaadinPageVisibility;
export {};
@@ -0,0 +1,69 @@
/*
* Copyright 2000-2026 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
// Firefox defers the visibilitychange event while the window is blurred, so
// a blur handler needs to wait long enough for that delivery to land before
// concluding the state is really "visible but not focused".
const FIREFOX_BLUR_SETTLE_MS = 500;
const DEFAULT_BLUR_SETTLE_MS = 10;
/**
* Returns the current visibility state synchronously. Used by the bootstrap
* path to seed the server-side signal without waiting for a DOM event.
*/
export function currentVisibility() {
if (document.hidden) {
return 'HIDDEN';
}
return document.hasFocus() ? 'VISIBLE' : 'VISIBLE_NOT_FOCUSED';
}
function isFirefox() {
// Firefox is the only supported browser that reorders visibilitychange
// relative to blur; UA sniffing is acceptable here because the alternative
// is waiting the longer interval on every browser.
return navigator.userAgent.indexOf('Firefox') > -1;
}
let blurTimer;
// Dispatch on document.body so the server-side Page facade (listening on
// the UI element, which is body) can update its signal.
function dispatch(state) {
document.body.dispatchEvent(new CustomEvent('vaadin-page-visibility-change', { detail: state }));
}
function clearBlurTimer() {
if (blurTimer !== undefined) {
clearTimeout(blurTimer);
blurTimer = undefined;
}
}
document.addEventListener('visibilitychange', () => {
clearBlurTimer();
dispatch(document.hidden ? 'HIDDEN' : 'VISIBLE');
});
window.addEventListener('blur', () => {
clearBlurTimer();
const delay = isFirefox() ? FIREFOX_BLUR_SETTLE_MS : DEFAULT_BLUR_SETTLE_MS;
blurTimer = setTimeout(() => {
blurTimer = undefined;
if (!document.hidden) {
dispatch('VISIBLE_NOT_FOCUSED');
}
}, delay);
});
window.addEventListener('focus', () => {
clearBlurTimer();
if (!document.hidden) {
dispatch('VISIBLE');
}
});
//# sourceMappingURL=PageVisibility.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PageVisibility.js","sourceRoot":"","sources":["../../../../src/main/frontend/PageVisibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,4EAA4E;AAC5E,4EAA4E;AAC5E,4DAA4D;AAC5D,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC;AACjE,CAAC;AAED,SAAS,SAAS;IAChB,uEAAuE;IACvE,2EAA2E;IAC3E,mDAAmD;IACnD,OAAO,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,SAAoD,CAAC;AAEzD,yEAAyE;AACzE,wDAAwD;AACxD,SAAS,QAAQ,CAAC,KAA2B;IAC3C,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,+BAA+B,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,cAAc;IACrB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,SAAS,GAAG,SAAS,CAAC;IACxB,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjD,cAAc,EAAE,CAAC;IACjB,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;IACnC,cAAc,EAAE,CAAC;IACjB,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAC5E,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;QAC1B,SAAS,GAAG,SAAS,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;IACpC,cAAc,EAAE,CAAC;IACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["/*\n * Copyright 2000-2026 Vaadin Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\ntype VaadinPageVisibility = 'VISIBLE' | 'VISIBLE_NOT_FOCUSED' | 'HIDDEN';\n\n// Firefox defers the visibilitychange event while the window is blurred, so\n// a blur handler needs to wait long enough for that delivery to land before\n// concluding the state is really \"visible but not focused\".\nconst FIREFOX_BLUR_SETTLE_MS = 500;\nconst DEFAULT_BLUR_SETTLE_MS = 10;\n\n/**\n * Returns the current visibility state synchronously. Used by the bootstrap\n * path to seed the server-side signal without waiting for a DOM event.\n */\nexport function currentVisibility(): VaadinPageVisibility {\n if (document.hidden) {\n return 'HIDDEN';\n }\n return document.hasFocus() ? 'VISIBLE' : 'VISIBLE_NOT_FOCUSED';\n}\n\nfunction isFirefox(): boolean {\n // Firefox is the only supported browser that reorders visibilitychange\n // relative to blur; UA sniffing is acceptable here because the alternative\n // is waiting the longer interval on every browser.\n return navigator.userAgent.indexOf('Firefox') > -1;\n}\n\nlet blurTimer: ReturnType<typeof setTimeout> | undefined;\n\n// Dispatch on document.body so the server-side Page facade (listening on\n// the UI element, which is body) can update its signal.\nfunction dispatch(state: VaadinPageVisibility): void {\n document.body.dispatchEvent(new CustomEvent('vaadin-page-visibility-change', { detail: state }));\n}\n\nfunction clearBlurTimer(): void {\n if (blurTimer !== undefined) {\n clearTimeout(blurTimer);\n blurTimer = undefined;\n }\n}\n\ndocument.addEventListener('visibilitychange', () => {\n clearBlurTimer();\n dispatch(document.hidden ? 'HIDDEN' : 'VISIBLE');\n});\n\nwindow.addEventListener('blur', () => {\n clearBlurTimer();\n const delay = isFirefox() ? FIREFOX_BLUR_SETTLE_MS : DEFAULT_BLUR_SETTLE_MS;\n blurTimer = setTimeout(() => {\n blurTimer = undefined;\n if (!document.hidden) {\n dispatch('VISIBLE_NOT_FOCUSED');\n }\n }, delay);\n});\n\nwindow.addEventListener('focus', () => {\n clearBlurTimer();\n if (!document.hidden) {\n dispatch('VISIBLE');\n }\n});\n"]}

Some files were not shown because too many files have changed in this diff Show More