4 Commits

Author SHA1 Message Date
Thomas Peetz d80aefe6b2 add publishing of kontor-model
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2026-08-26 18:33:35 +02:00
Thomas Peetz 652378e7de update kontor-api
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2026-08-25 17:45:03 +02:00
tpeetz 57b942a9ca update Vaadin to 25.2.6 (#95)
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
Co-authored-by: Thomas Peetz <thomas.peetz@cimt-ag.de>
Reviewed-on: #95
2026-08-25 14:15:18 +00:00
Thomas Peetz bda453a92a change to version 0.4.0-SNAPSHOT
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2026-08-25 11:11:51 +02:00
147 changed files with 10263 additions and 1452 deletions
+1
View File
@@ -11,3 +11,4 @@ kontor-robyn.bak
kontor-robyn.bak2
kontor-data/.gradle
kontor-data/build/
/kontor-api/kontor-api.code-workspace
+1 -1
View File
@@ -4,7 +4,7 @@ NEXUS_URL=https://nexus.thpeetz.de
MAVEN_REPO=maven-snapshots
GROUP_ID=de.thpeetz
ARTIFACT_ID=kontor-spring
VERSION=0.3.0
VERSION=0.4.0
FILE_EXTENSION=jar
download_url=$(curl -X GET "${NEXUS_URL}/service/rest/v1/search/assets?repository=${MAVEN_REPO}&maven.groupId=${GROUP_ID}&maven.artifactId=${ARTIFACT_ID}&maven.baseVersion=${VERSION}&maven.extension=${FILE_EXTENSION}" -H "accept: application/json" | jq -rc '.items | .[].downloadUrl' | sort | tail -n 1)
-3
View File
@@ -14,9 +14,6 @@ ENV PATH="/root/.local/bin:${PATH}"
WORKDIR /app
COPY ./pyproject.toml .
#RUN --mount=type=bind,source=/home/tpeetz/projects/kontor/kontor-model,target=/container/kontor-model uv add /container/kontor-model
#COPY ../kontor-model/ /container
RUN uv add /container/kontor-model
RUN uv sync
# ------------------------------- Production Stage ------------------------------ ##
+11 -3
View File
@@ -1,6 +1,6 @@
[project]
name = "kontor-api"
version = "0.1.0"
version = "0.4.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
@@ -32,5 +32,13 @@ dependencies = [
"kontor-model",
]
[tool.uv.sources]
kontor-model = { path = "../kontor-model", editable = true }
[[tool.uv.index]]
name = "nexus"
url = "https://nexus.thpeetz.de/repository/pypi-group/simple"
publish-url = "https://nexus.thpeetz.de/repository/pypi-internal/"
default = true
[[tool.uv.index]]
name = "nexus-proxy"
url = "https://nexus.thpeetz.de/repository/pypi-proxy/simple"
+51 -2
View File
@@ -1,11 +1,21 @@
from typing import List
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, status, HTTPException
from kontor_model.db.models.media import MediaLofi
from src.db.session import SessionDep
from kontor_model.schema.media.lofi import MediaLofiResponse, lofi_to_response
from src.core.log_conf import logger
from kontor_model.schema.media.lofi import (
MediaLofiModel,
MediaLofiResponse,
lofi_to_response,
lofi_to_model
)
from kontor_model.db.repository.media.lofi import (
import_medialofi,
delete_medialofi
)
router = APIRouter()
@@ -40,3 +50,42 @@ def get_lofi(lofi_id: str, db: SessionDep) -> MediaLofiResponse:
raise HTTPException(status_code=404, detail="MediaLofi could not be found")
response = lofi_to_response(lofi)
return response
@router.delete("/lofi/{lofi_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_lofi(lofi_id: str, db: SessionDep):
"""
Delete MediaFile by given id.
"""
lofi = db.get(MediaLofi, lofi_id)
if not lofi:
raise HTTPException(status_code=404, detail="MediaLofi could not be found")
logger.info("delete MediaLofi: %s", lofi_id)
delete_medialofi(db=db, lofi_id=lofi.id)
@router.put("/lofi/{lofi_id}", response_model=MediaLofiResponse)
def update_lofi(lofi_id: str, db: SessionDep, info: MediaLofiResponse) -> MediaLofiResponse:
"""
Update MediaLofi with given id and data.
"""
media_lofi = db.get(MediaLofi, lofi_id)
if not media_lofi:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="MediaLofi could not be found")
lofi_to_model(info, media_lofi)
db.add(media_lofi)
db.commit()
media_lofi = db.get(MediaLofi, lofi_id)
if not media_lofi:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="MediaLofi could not be found")
response = lofi_to_response(media_lofi)
return response
@router.post("/lofi", status_code=status.HTTP_201_CREATED)
def add_lofi(new_lofi: MediaLofiModel, db: SessionDep) -> MediaLofiResponse:
logger.info("add medialofi %s", new_lofi)
try:
medialofi: MediaLofi = import_medialofi(db, new_lofi)
except:
raise HTTPException(status_code=409, detail="MediaLofi duplicate")
response = lofi_to_response(medialofi)
return response
+9 -5
View File
@@ -9,17 +9,21 @@ load_dotenv(dotenv_path=env_path)
class Settings:
PROJECT_NAME: str = "Kontor"
PROJECT_VERSION: str = "0.3.0"
PROJECT_VERSION: str = "0.4.0"
DB_USER: str = os.getenv("DB_USER", "kontor")
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
DB_PORT: int = int(os.getenv("DB_PORT", 5432))
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=")
DATABASE_URL: str = (
f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
)
SECRET_KEY: str = os.getenv(
"SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc="
)
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60*24*7 # one week in mins
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # one week in mins
settings = Settings()
+1007 -596
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,7 +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
version=0.4.0-SNAPSHOT
group=de.thpeetz
org.gradle.configuration-cache=true
+12 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "kontor_model"
version = "0.3.0"
version = "0.4.0"
description = "Kontor Model"
readme = "README.md"
authors = [
@@ -16,3 +16,14 @@ dependencies = [
[build-system]
requires = ["uv_build>=0.9.27,<0.10.0"]
build-backend = "uv_build"
[[tool.uv.index]]
name = "nexus"
url = "https://nexus.thpeetz.de/repository/pypi-group/simple"
publish-url = "https://nexus.thpeetz.de/repository/pypi-internal/"
default = true
[[tool.uv.index]]
name = "nexus-proxy"
url = "https://nexus.thpeetz.de/repository/pypi-proxy/simple"
@@ -4,10 +4,14 @@ import uuid
from sqlalchemy.orm import Session
from kontor_model.db.models.media import MediaLofi
from kontor_model.schema.media.lofi import MediaLofiModel
def create_new_lofi(url: str, db: Session) -> MediaLofi:
print(url)
"""
Create MediaLofi with given URL.
"""
print("create MediaLofi with url %s", url)
media_lofi = MediaLofi()
media_lofi.id = str(uuid.uuid4())
media_lofi.url = url
@@ -20,3 +24,46 @@ def create_new_lofi(url: str, db: Session) -> MediaLofi:
db.refresh(media_lofi)
print(media_lofi)
return media_lofi
def import_medialofi(db: Session, new_lofi: MediaLofiModel) -> MediaLofi:
"""
import MediaLofi and set missing values with default ones.
"""
print("import MediaLofi with %s", new_lofi)
media_lofi: MediaLofi = MediaLofi()
media_lofi.id = new_lofi.id
if new_lofi.created_date:
media_lofi.created_date = new_lofi.created_date
else:
media_lofi.created_date = datetime.now()
if new_lofi.last_modified_date:
media_lofi.last_modified_date = new_lofi.last_modified_date
else:
media_lofi.last_modified_date = datetime.now()
media_lofi.version = new_lofi.version
if new_lofi.title:
media_lofi.title = new_lofi.title
else:
media_lofi.title = ""
if new_lofi.url:
media_lofi.url = new_lofi.url
else:
# TODO shoud exeception be raised when url is missing?
media_lofi.url = ""
media_lofi.review = new_lofi.review
media_lofi.should_download = new_lofi.should_download
db.add(media_lofi)
db.commit()
db.refresh(media_lofi)
return media_lofi
def delete_medialofi(db: Session, lofi_id: str):
"""
Delete MediaLofi with given ID from db.
"""
print("delete MediaLofi with id %s", lofi_id)
lofi = db.get(MediaLofi, lofi_id)
db.delete(lofi)
db.commit()
@@ -9,15 +9,49 @@ class MediaLofiResponse(BaseModel):
created_date: datetime
last_modified_date: datetime
version: int
title: str = ""
file_name: str = ""
url: str = ""
review: bool = False
should_download: bool = False
title: str = ""
url: str = ""
class AddLofi(BaseModel):
url: str
class MediaLofiModel(BaseModel):
"""
Pydantic Model to import MediaLofi.
"""
id: str
created_date: datetime
last_modified_date: datetime
version: int = 0
title: str = ""
file_name: str = ""
url: str = ""
review: bool = False
should_download: bool = False
def lofi_to_model(model: MediaLofiResponse, medialofi: MediaLofi) -> MediaLofi:
"""
Set data of response to model
"""
medialofi.file_name = model.file_name
if model.url is not None:
medialofi.url = model.url
else:
medialofi.url = ""
if model.title is not None:
medialofi.title = model.title
else:
medialofi.title = ""
medialofi.last_modified_date = datetime.now()
medialofi.review = model.review
medialofi.should_download = model.should_download
return medialofi
def lofi_to_response(lofi: MediaLofi) -> MediaLofiResponse:
response: MediaLofiResponse = MediaLofiResponse(
id=lofi.id,
+187 -187
View File
@@ -5,93 +5,93 @@ 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" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/annotated-types/0.8.0/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/annotated-types/0.8.0/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" },
]
[[package]]
name = "dnspython"
version = "2.8.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/dnspython/2.8.0/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/dnspython/2.8.0/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
]
[[package]]
name = "email-validator"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
dependencies = [
{ name = "dnspython" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/email-validator/2.3.0/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/email-validator/2.3.0/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
]
[[package]]
name = "greenlet"
version = "3.5.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0b/d8/7cc97c142388aef03f622e001c572c4f84e9252a439549d483f555771970/greenlet-3.5.5.tar.gz", hash = "sha256:adb4bae02e91a8e863e48b177e4014bdcac8a6b5e047ea1df687a61534b85e6c", size = 207585, upload-time = "2026-08-10T15:09:36.136Z" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5.tar.gz", hash = "sha256:adb4bae02e91a8e863e48b177e4014bdcac8a6b5e047ea1df687a61534b85e6c", size = 207585, upload-time = "2026-08-10T15:09:36.136Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/3d/8cef5f724ec0d4add2af8961d504535ec60c3cca9e464f6d03bdba29d85b/greenlet-3.5.5-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:b79fd2a5bc099b5e744f34c4c9a58954a5f4cb7529fb4b6e8446057d61b6edaa", size = 294730, upload-time = "2026-08-10T13:27:51.206Z" },
{ url = "https://files.pythonhosted.org/packages/88/4b/8e7aa3f514273aecff30a16ab1bac09ff54cfc7e6860fdd8058c37ff2499/greenlet-3.5.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:634cf15a233a949136879dd388e25d3296e16f3f1e217d2456797b8579ebc6ed", size = 614536, upload-time = "2026-08-10T14:14:36.589Z" },
{ url = "https://files.pythonhosted.org/packages/85/48/4e95e9dd5a8a397dc6a6345dd7f1935113d0fca4f85e89d3976da9cd988d/greenlet-3.5.5-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:499adea519f748407fc6806d20eedabac2884fd73b9f38d81236e190ba20dfef", size = 626924, upload-time = "2026-08-10T14:27:27.048Z" },
{ url = "https://files.pythonhosted.org/packages/89/5d/398a1c71fa7a277deeb376c999979de6786f08fc2d5747a0b9d6e11738dd/greenlet-3.5.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2eabb980975cba5b93a95f6f69287d05fc05ac955bfd6a320a7c083eeb52c0b0", size = 623906, upload-time = "2026-08-10T13:40:50.501Z" },
{ url = "https://files.pythonhosted.org/packages/04/1b/745450fc5ea9e0cb17d840d248f284db3363de736d362c7d2d883e3eadba/greenlet-3.5.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03115c2e0a371999bf8ae616aa8d653f96641d4705c457aebaa187276e9f7537", size = 1581430, upload-time = "2026-08-10T14:15:06.853Z" },
{ url = "https://files.pythonhosted.org/packages/d4/29/d51b296e3191bb15d3d81ec375af1909e4466c0f395d744ed475801798a9/greenlet-3.5.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4441153ffba21b90d3ca89fe3d31f5c093ae6c0bf0cfdfc98f54cde22f95b62e", size = 1645684, upload-time = "2026-08-10T13:40:32.133Z" },
{ url = "https://files.pythonhosted.org/packages/12/63/369f1a1625e64e9e31df3963c6044056e3fdfa3fa3fdba3c54ffefa6e987/greenlet-3.5.5-cp313-cp313-win_amd64.whl", hash = "sha256:95c5b1f4b3a193f8a0c2de4bfdcb48d119f7f1063941f1de1f2168051b3e52dd", size = 324075, upload-time = "2026-08-10T13:26:58.974Z" },
{ url = "https://files.pythonhosted.org/packages/45/78/649cb5c09d4d81f6dd1444e75474a7206784743283a21d24171562ac4899/greenlet-3.5.5-cp313-cp313-win_arm64.whl", hash = "sha256:1af90aa4bc129883b340cdd6957a3bc74f60528a4993bbd1f53aaebe1d9981cc", size = 308260, upload-time = "2026-08-10T13:27:50.795Z" },
{ url = "https://files.pythonhosted.org/packages/7f/8c/080e881fa2be95ff1ddbd6994b2bab3b1a78df3b3fcab39306011764fcc7/greenlet-3.5.5-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d4a389a852e392a6366058651a20fa5ba40d979865aa81bea2ccbdc44805070d", size = 295309, upload-time = "2026-08-10T13:26:03.032Z" },
{ url = "https://files.pythonhosted.org/packages/25/cc/0ac614e6586c0e42d4cc281a5819150f4f43685744a4c5ff77139286409d/greenlet-3.5.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70b157cd319873e8b544ddc2de158f55bbd0a9b0218c8ce9332039801518e328", size = 661185, upload-time = "2026-08-10T14:14:37.867Z" },
{ url = "https://files.pythonhosted.org/packages/5e/b9/6808725354be8ad305dfe5172377664fc9642d4fc043be246b3314cf4482/greenlet-3.5.5-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8bdfd1424abcf26832961e766570cae79efdb9599d709088c9cb6ef82b194926", size = 673419, upload-time = "2026-08-10T14:27:28.652Z" },
{ url = "https://files.pythonhosted.org/packages/42/2e/40c509967da7f254680826a2fa0dd22138ec79946c70b97542d74cde8b43/greenlet-3.5.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:182de51c6b572a705f2fafaab2e783bcf7d2760940229dfe73086cbae037af3e", size = 670822, upload-time = "2026-08-10T13:40:51.833Z" },
{ url = "https://files.pythonhosted.org/packages/2d/22/c3c2eee4a8fe191d6d1d183086c56133d646024e3d70bfd414829f64560b/greenlet-3.5.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8fec3f165dfe332e490c3247c0f6c23b0bfc45f06496ad7f00ddb00e3d35e4dc", size = 1628469, upload-time = "2026-08-10T14:15:08.11Z" },
{ url = "https://files.pythonhosted.org/packages/f7/87/25babd09b94cb1f03e71db815fde463f0262e40cfbd953d58a8d77311351/greenlet-3.5.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c6ce25fee6cabc8bf22cb8b52e642cbb821be5b9aec8094d07ff03378141b8e9", size = 1691952, upload-time = "2026-08-10T13:40:33.502Z" },
{ url = "https://files.pythonhosted.org/packages/2e/3d/5cc9701117ea4dc0eb7bf1f4f9b7888a6e2e5277ddfae095805ace50f2b6/greenlet-3.5.5-cp314-cp314-win_amd64.whl", hash = "sha256:7dffc5c859fe6059974df1e37d7923d654a83e2ae18fdd616994270e001115e1", size = 327458, upload-time = "2026-08-10T13:27:02.868Z" },
{ url = "https://files.pythonhosted.org/packages/a7/6b/594fa2de7fae7629168a404a4305d7d7e31a5742c50a801b1839543cb93d/greenlet-3.5.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e2afcfc4d4305dd715809b03da5cbe437c8984f61d8917751eb5fe4aefa3e07", size = 311146, upload-time = "2026-08-10T13:27:25.046Z" },
{ url = "https://files.pythonhosted.org/packages/24/e0/50cd600b469e5734c72709b6b1838b6bc63f307b573c772c3132d6ecfe92/greenlet-3.5.5-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:0e5a7de979d764aea1f5b6e95cf92b5b37741b9823702041f34b126e7f690277", size = 305471, upload-time = "2026-08-10T13:26:20.568Z" },
{ url = "https://files.pythonhosted.org/packages/75/a3/77acd66dfc6387b5219b2080806c0cabb73c10eb1bb44b413c40a62015ba/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef01bd457f11fc158b130ca0027a3c365693280e8e231b65bdaf57999f39f5b", size = 672470, upload-time = "2026-08-10T14:14:39.058Z" },
{ url = "https://files.pythonhosted.org/packages/b9/71/0d178142dca3ec19f46fb2212ae73d30ad53b9d548dc64804086033a7089/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5173a72310725a74afc82c164f0e52cb8ad0de62f2bb623f24f6c0cc07d80272", size = 679973, upload-time = "2026-08-10T14:27:30.072Z" },
{ url = "https://files.pythonhosted.org/packages/6e/31/46eb8567302eaf787abf88d09df014e14ae3baf460af1b8b0efdbd3efcd5/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44f08341873200ba8a60a8bc14ace3d91f1754f7fa7bc66157714a8cd420a476", size = 676634, upload-time = "2026-08-10T13:40:53.004Z" },
{ url = "https://files.pythonhosted.org/packages/a3/e9/b88bbf5b29970cb84172dc2c32aa3e5e579ceb94c808e81c826454138850/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d246c0db9a2513cd45f019ba178ea4d4d4705bd210ee465e2c15d76a1ab13874", size = 1637320, upload-time = "2026-08-10T14:15:09.317Z" },
{ url = "https://files.pythonhosted.org/packages/6d/8c/7631ed29cc6f0392f11830076e172ce4885e70b0bc2c1bce1731176d4b4e/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:72507285b5caa1d17904a3f7c322ca780823a54170a0e04ec3f37bcc60d4db71", size = 1697412, upload-time = "2026-08-10T13:40:34.924Z" },
{ url = "https://files.pythonhosted.org/packages/da/0f/f7dd935f9c4cb1be49098770587f54d8a78518e55c89bce86c4fb4109057/greenlet-3.5.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7805655781fb8f28a55d05fe57ed61f5f10f1892fb587673e3bb5264f28041f0", size = 331514, upload-time = "2026-08-10T13:29:20.611Z" },
{ url = "https://files.pythonhosted.org/packages/b7/e5/681b01f8fbc1b55232822f99e8f8afeb78a55a7c76a7bf9dbdc7ccb03a6d/greenlet-3.5.5-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:c0db80fcd5b8aece93f66c64f78a786bbb6b96c5fe63ef5a5a4581ecf8bab206", size = 295975, upload-time = "2026-08-10T13:28:45.985Z" },
{ url = "https://files.pythonhosted.org/packages/11/f2/69b488cd9e7267bf4b0fe8cdebf25d8d6df680d21bdf41150d23e23d6652/greenlet-3.5.5-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b241c32f912ada659808d68e308c568baf577eebf757d15471472de0c18cfad", size = 666823, upload-time = "2026-08-10T14:14:40.222Z" },
{ url = "https://files.pythonhosted.org/packages/84/d4/d5bc2fdebbdda0c94555925ba79948b8395d75a7f6a36cc85dce5bab9f11/greenlet-3.5.5-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ef6a08349401d8eaf3cb12688ac8557de95788556b8631ef17555a4a173022c0", size = 677613, upload-time = "2026-08-10T14:27:31.543Z" },
{ url = "https://files.pythonhosted.org/packages/bd/93/542d8a3a90f3b35c6ad8bf7e56a03010287f2cafa289a5b7985b5207db39/greenlet-3.5.5-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2e3d061b8e13aec2f0441689b3c71b244a20e5d274a52cb0f7e31bd1d139552", size = 675930, upload-time = "2026-08-10T13:40:54.205Z" },
{ url = "https://files.pythonhosted.org/packages/52/b5/89c9f2e8460d71101037d47a1feed11928615a5edd42370be290e0657eeb/greenlet-3.5.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:9ab5f5b93655e77fe0d6c2dfd22b5eac751bb1f876d8ec21761b7c1fb9266007", size = 1633878, upload-time = "2026-08-10T14:15:10.693Z" },
{ url = "https://files.pythonhosted.org/packages/b8/60/297de93f3b02ac78a5e04d32bb8bbe3080f4a73d8ed95016561463b70618/greenlet-3.5.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f0e5a21bd4452a88cf032fc43c4a5b307ab1380eacb63b5988f9c0317885e773", size = 1696597, upload-time = "2026-08-10T13:40:36.252Z" },
{ url = "https://files.pythonhosted.org/packages/18/25/54c6eaff4f337fb670215e89eb2d00d9499487b658e709d4b477be4a342e/greenlet-3.5.5-cp315-cp315-win_amd64.whl", hash = "sha256:469dbb0a78625642f4a626cfd0c6e8bccc0385b5e49189b6308bbe849ec88a8e", size = 327700, upload-time = "2026-08-10T13:28:06.752Z" },
{ url = "https://files.pythonhosted.org/packages/67/67/857e88a36301caa0e029870132c2478bd55d896630321432afab03a3115f/greenlet-3.5.5-cp315-cp315-win_arm64.whl", hash = "sha256:2d57406c3efd32d7a81e17a674314e8bd00792cdab49ea3228a49aa1bfb2e769", size = 311750, upload-time = "2026-08-10T13:34:08.815Z" },
{ url = "https://files.pythonhosted.org/packages/10/e2/3144c0a116067ac1e30457b0139a94d60d1d36a86e015de68e9ac87cb3bc/greenlet-3.5.5-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:68184dfcf50ccaa8e864770fe0633a7e27250ea9329f8192ef47ee9ecfd78e1c", size = 306387, upload-time = "2026-08-10T13:27:00.897Z" },
{ url = "https://files.pythonhosted.org/packages/5c/a1/cb4223a7e9b9f43b8807e8eb212358bfe2dfaa174a9ea2889eb1714dcba2/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ec0dc0e59dc9c61af5c47348365ccbbd7addfafe0a93b00336ff3da2907bdc6", size = 676472, upload-time = "2026-08-10T14:14:41.417Z" },
{ url = "https://files.pythonhosted.org/packages/9e/cd/a154b4498e5d8f12ada291cfb3b8d596eadde2177f5bf09a9be699d2a446/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e604f58e35833fc46ef20302bcb314dddbfd3fcf33a4f936216d51dd678d63ae", size = 684238, upload-time = "2026-08-10T14:27:32.946Z" },
{ url = "https://files.pythonhosted.org/packages/bf/bb/b0031d260c2968a3c87deebc51d80c64e499377f993aafe06ee3b7488cc2/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40239b5384f96da3963585cc6d7eaa9b56f8ae67e8d92cc82dd9e202fc847de3", size = 681246, upload-time = "2026-08-10T13:40:55.402Z" },
{ url = "https://files.pythonhosted.org/packages/9a/07/da554b71ab88e649da146e1065d86a48a5c5d92e50ab74ef41b504aa7f56/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:a1eaccf5c3a1d3e46dead602c72e6836731e8e245c9de6a27764567b6b62d4c0", size = 1642735, upload-time = "2026-08-10T14:15:11.92Z" },
{ url = "https://files.pythonhosted.org/packages/78/76/26a3782a051677668af9d92beaa47cd87ba9dd5072f762961144a03dd4c6/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:19e4e026fe20691f333b8eb1a3bc9625eceba8c3f9d62ec5a6f8581afbc6b5a5", size = 1700925, upload-time = "2026-08-10T13:40:37.656Z" },
{ url = "https://files.pythonhosted.org/packages/28/d9/fe7baf4190c2ae71f267efb9de21b3172bb35bc0ed1ef53dd6027d658e33/greenlet-3.5.5-cp315-cp315t-win_amd64.whl", hash = "sha256:712aee154f648bde84634654bb38bb78c69ac640c37a45c9effed800735049d8", size = 331829, upload-time = "2026-08-10T13:26:48.851Z" },
{ url = "https://files.pythonhosted.org/packages/df/af/419a4e383bd600858a9b67e9b280a60fdc383ee3f2fe5b6c0c1ef04e74d1/greenlet-3.5.5-cp315-cp315t-win_arm64.whl", hash = "sha256:7f049911ee81a16a03c33d5450d8d5867d27f596ca5fb201b86f4524e874468b", size = 315093, upload-time = "2026-08-10T13:29:34.949Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:b79fd2a5bc099b5e744f34c4c9a58954a5f4cb7529fb4b6e8446057d61b6edaa", size = 294730, upload-time = "2026-08-10T13:27:51.206Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:634cf15a233a949136879dd388e25d3296e16f3f1e217d2456797b8579ebc6ed", size = 614536, upload-time = "2026-08-10T14:14:36.589Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:499adea519f748407fc6806d20eedabac2884fd73b9f38d81236e190ba20dfef", size = 626924, upload-time = "2026-08-10T14:27:27.048Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2eabb980975cba5b93a95f6f69287d05fc05ac955bfd6a320a7c083eeb52c0b0", size = 623906, upload-time = "2026-08-10T13:40:50.501Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03115c2e0a371999bf8ae616aa8d653f96641d4705c457aebaa187276e9f7537", size = 1581430, upload-time = "2026-08-10T14:15:06.853Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4441153ffba21b90d3ca89fe3d31f5c093ae6c0bf0cfdfc98f54cde22f95b62e", size = 1645684, upload-time = "2026-08-10T13:40:32.133Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-win_amd64.whl", hash = "sha256:95c5b1f4b3a193f8a0c2de4bfdcb48d119f7f1063941f1de1f2168051b3e52dd", size = 324075, upload-time = "2026-08-10T13:26:58.974Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp313-cp313-win_arm64.whl", hash = "sha256:1af90aa4bc129883b340cdd6957a3bc74f60528a4993bbd1f53aaebe1d9981cc", size = 308260, upload-time = "2026-08-10T13:27:50.795Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d4a389a852e392a6366058651a20fa5ba40d979865aa81bea2ccbdc44805070d", size = 295309, upload-time = "2026-08-10T13:26:03.032Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70b157cd319873e8b544ddc2de158f55bbd0a9b0218c8ce9332039801518e328", size = 661185, upload-time = "2026-08-10T14:14:37.867Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8bdfd1424abcf26832961e766570cae79efdb9599d709088c9cb6ef82b194926", size = 673419, upload-time = "2026-08-10T14:27:28.652Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:182de51c6b572a705f2fafaab2e783bcf7d2760940229dfe73086cbae037af3e", size = 670822, upload-time = "2026-08-10T13:40:51.833Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8fec3f165dfe332e490c3247c0f6c23b0bfc45f06496ad7f00ddb00e3d35e4dc", size = 1628469, upload-time = "2026-08-10T14:15:08.11Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c6ce25fee6cabc8bf22cb8b52e642cbb821be5b9aec8094d07ff03378141b8e9", size = 1691952, upload-time = "2026-08-10T13:40:33.502Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-win_amd64.whl", hash = "sha256:7dffc5c859fe6059974df1e37d7923d654a83e2ae18fdd616994270e001115e1", size = 327458, upload-time = "2026-08-10T13:27:02.868Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e2afcfc4d4305dd715809b03da5cbe437c8984f61d8917751eb5fe4aefa3e07", size = 311146, upload-time = "2026-08-10T13:27:25.046Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:0e5a7de979d764aea1f5b6e95cf92b5b37741b9823702041f34b126e7f690277", size = 305471, upload-time = "2026-08-10T13:26:20.568Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef01bd457f11fc158b130ca0027a3c365693280e8e231b65bdaf57999f39f5b", size = 672470, upload-time = "2026-08-10T14:14:39.058Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5173a72310725a74afc82c164f0e52cb8ad0de62f2bb623f24f6c0cc07d80272", size = 679973, upload-time = "2026-08-10T14:27:30.072Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44f08341873200ba8a60a8bc14ace3d91f1754f7fa7bc66157714a8cd420a476", size = 676634, upload-time = "2026-08-10T13:40:53.004Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d246c0db9a2513cd45f019ba178ea4d4d4705bd210ee465e2c15d76a1ab13874", size = 1637320, upload-time = "2026-08-10T14:15:09.317Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:72507285b5caa1d17904a3f7c322ca780823a54170a0e04ec3f37bcc60d4db71", size = 1697412, upload-time = "2026-08-10T13:40:34.924Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7805655781fb8f28a55d05fe57ed61f5f10f1892fb587673e3bb5264f28041f0", size = 331514, upload-time = "2026-08-10T13:29:20.611Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:c0db80fcd5b8aece93f66c64f78a786bbb6b96c5fe63ef5a5a4581ecf8bab206", size = 295975, upload-time = "2026-08-10T13:28:45.985Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b241c32f912ada659808d68e308c568baf577eebf757d15471472de0c18cfad", size = 666823, upload-time = "2026-08-10T14:14:40.222Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ef6a08349401d8eaf3cb12688ac8557de95788556b8631ef17555a4a173022c0", size = 677613, upload-time = "2026-08-10T14:27:31.543Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2e3d061b8e13aec2f0441689b3c71b244a20e5d274a52cb0f7e31bd1d139552", size = 675930, upload-time = "2026-08-10T13:40:54.205Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:9ab5f5b93655e77fe0d6c2dfd22b5eac751bb1f876d8ec21761b7c1fb9266007", size = 1633878, upload-time = "2026-08-10T14:15:10.693Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f0e5a21bd4452a88cf032fc43c4a5b307ab1380eacb63b5988f9c0317885e773", size = 1696597, upload-time = "2026-08-10T13:40:36.252Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-win_amd64.whl", hash = "sha256:469dbb0a78625642f4a626cfd0c6e8bccc0385b5e49189b6308bbe849ec88a8e", size = 327700, upload-time = "2026-08-10T13:28:06.752Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315-win_arm64.whl", hash = "sha256:2d57406c3efd32d7a81e17a674314e8bd00792cdab49ea3228a49aa1bfb2e769", size = 311750, upload-time = "2026-08-10T13:34:08.815Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:68184dfcf50ccaa8e864770fe0633a7e27250ea9329f8192ef47ee9ecfd78e1c", size = 306387, upload-time = "2026-08-10T13:27:00.897Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ec0dc0e59dc9c61af5c47348365ccbbd7addfafe0a93b00336ff3da2907bdc6", size = 676472, upload-time = "2026-08-10T14:14:41.417Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e604f58e35833fc46ef20302bcb314dddbfd3fcf33a4f936216d51dd678d63ae", size = 684238, upload-time = "2026-08-10T14:27:32.946Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40239b5384f96da3963585cc6d7eaa9b56f8ae67e8d92cc82dd9e202fc847de3", size = 681246, upload-time = "2026-08-10T13:40:55.402Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:a1eaccf5c3a1d3e46dead602c72e6836731e8e245c9de6a27764567b6b62d4c0", size = 1642735, upload-time = "2026-08-10T14:15:11.92Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:19e4e026fe20691f333b8eb1a3bc9625eceba8c3f9d62ec5a6f8581afbc6b5a5", size = 1700925, upload-time = "2026-08-10T13:40:37.656Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-win_amd64.whl", hash = "sha256:712aee154f648bde84634654bb38bb78c69ac640c37a45c9effed800735049d8", size = 331829, upload-time = "2026-08-10T13:26:48.851Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/greenlet/3.5.5/greenlet-3.5.5-cp315-cp315t-win_arm64.whl", hash = "sha256:7f049911ee81a16a03c33d5450d8d5867d27f596ca5fb201b86f4524e874468b", size = 315093, upload-time = "2026-08-10T13:29:34.949Z" },
]
[[package]]
name = "idna"
version = "3.18"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/idna/3.18/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/idna/3.18/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
[[package]]
name = "kontor-model"
version = "0.3.0"
version = "0.4.0"
source = { editable = "." }
dependencies = [
{ name = "msgspec", extra = ["toml", "yaml"] },
@@ -109,33 +109,33 @@ requires-dist = [
[[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" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/msgspec-0.21.1-cp313-cp313-win_amd64.whl", hash = "sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a", size = 189857, upload-time = "2026-04-12T21:44:25.359Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/msgspec-0.21.1-cp313-cp313-win_arm64.whl", hash = "sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898", size = 175403, upload-time = "2026-04-12T21:44:26.608Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/msgspec-0.21.1-cp314-cp314-win_amd64.whl", hash = "sha256:5666b1b560b97b6ec2eb3fca8a502298ebac56e13bbca1f88523538ce83d01ea", size = 193810, upload-time = "2026-04-12T21:44:36.612Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/msgspec-0.21.1-cp314-cp314-win_arm64.whl", hash = "sha256:d8b8578e4c83b14ceea4cef0d0b747e31d9330fe4b03b2b2ad4063866a178f93", size = 179125, upload-time = "2026-04-12T21:44:38.198Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/msgspec-0.21.1-cp314-cp314t-win_amd64.whl", hash = "sha256:740fbf1c9d59992ca3537d6fbe9ebbf9eaf726a65fbf31448e0ecbc710697a63", size = 206650, upload-time = "2026-04-12T21:44:47.601Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/msgspec/0.21.1/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]
@@ -149,16 +149,16 @@ yaml = [
[[package]]
name = "pydantic"
version = "2.13.4"
source = { registry = "https://pypi.org/simple" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/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" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic/2.13.4/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic/2.13.4/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
]
[package.optional-dependencies]
@@ -169,149 +169,149 @@ email = [
[[package]]
name = "pydantic-core"
version = "2.46.4"
source = { registry = "https://pypi.org/simple" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/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" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pydantic-core/2.46.4/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" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/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://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/pyyaml/6.0.3/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.52"
source = { registry = "https://pypi.org/simple" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/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/3b/21/77b4c147963073040dc3c3a5cb7a8c3001a1893c0209432cb77f9df836aa/sqlalchemy-2.0.52.tar.gz", hash = "sha256:5e2d46356ac2ccb7d268ab6c2319ac6a2b42f1b8d5fd8bd3d46855cd82abee97", size = 9945637, upload-time = "2026-08-11T19:07:09.829Z" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52.tar.gz", hash = "sha256:5e2d46356ac2ccb7d268ab6c2319ac6a2b42f1b8d5fd8bd3d46855cd82abee97", size = 9945637, upload-time = "2026-08-11T19:07:09.829Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/18/e30c6fe1eca1bf34a39fbdd6066121cc9974c850faf6f349eac563697a26/sqlalchemy-2.0.52-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2eb3c6a64b1bfe6704777cfd504e7b8ad093a5f3e03ce67663a5e6742f294e43", size = 2167724, upload-time = "2026-08-11T20:58:12.679Z" },
{ url = "https://files.pythonhosted.org/packages/d0/56/2e17d161a4f7ecc1c2ffb93e607b4e1898bb551b451b283235acb8f6ce47/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:923bb183c1dc64fdf7b717965e3d59938ec4f8b8710b419a21ce403e5da9a9e1", size = 3321189, upload-time = "2026-08-11T21:02:41.932Z" },
{ url = "https://files.pythonhosted.org/packages/cf/b8/8490916e893f3f8d74dc9cc54c078619364999dee37047a188e73abbc852/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:651d6d8782e80679e6151707c7b490834d46ada526328895abf567f25e63d29c", size = 3338185, upload-time = "2026-08-11T21:17:02.597Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f7/752cc8ee453da222829b3f5c4613614bf750d97429363b70414fa10478e4/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b08cddb8989775e3c88799d86704bdfc3ee6e9846118201aa5997f16f27e3a15", size = 3271698, upload-time = "2026-08-11T21:02:43.963Z" },
{ url = "https://files.pythonhosted.org/packages/51/e6/074ade0c07b9e4c8e8bca46820320ed94df9702afdb6f2af06623068d2e6/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab66fa9618269390d4dfa222f2f2f88f7bc4bf5da13905131b818217db7e8057", size = 3308936, upload-time = "2026-08-11T21:17:04.172Z" },
{ url = "https://files.pythonhosted.org/packages/66/07/557c0d04716705599227945ac14e0a17ad0338e899f37d8c2ddff4dcc663/sqlalchemy-2.0.52-cp313-cp313-win32.whl", hash = "sha256:c63bda077685c85ca513286547a531ba57e7a68cf0a7ed3bafcc2bbd18896f4d", size = 2127308, upload-time = "2026-08-11T21:14:53.879Z" },
{ url = "https://files.pythonhosted.org/packages/96/4e/226eda27654318ce525d043025221f689abef883da2c7126f9065121618c/sqlalchemy-2.0.52-cp313-cp313-win_amd64.whl", hash = "sha256:9876b09b9f1ce7398b0ffece585c0a911244c53191187341f6bcae640e133751", size = 2153876, upload-time = "2026-08-11T21:14:55.527Z" },
{ url = "https://files.pythonhosted.org/packages/d5/f5/71cb30af58c9b80a4e1fac0b73bb48f86d497a774a6a2eb6d2f1e657bb73/sqlalchemy-2.0.52-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:410d52be41d17f1a236d19520fbe776257dc16516ed06bd16d433311842aefd9", size = 2169537, upload-time = "2026-08-11T20:58:13.855Z" },
{ url = "https://files.pythonhosted.org/packages/4c/93/d07ebd645d1b07b6b5ed63450a70f063a346a7e0f2c8810daf2e532400cb/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfe9ce533dbe4d0a2ae1486546619bd30b76bcd670539a44d910361376175f5e", size = 3319606, upload-time = "2026-08-11T21:02:45.829Z" },
{ url = "https://files.pythonhosted.org/packages/ae/5c/290c84c7c2566ecd3b65baaae0fddec9bc33b033b398a06123bb86fbfc6e/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:812bae5138bfc0aa46fb0686da0fc7f581f68e2bbb05bc24c3713bebaedd1437", size = 3323642, upload-time = "2026-08-11T21:17:05.675Z" },
{ url = "https://files.pythonhosted.org/packages/13/f5/2cc160590ca49173359557880b92a0572293ccb899e8f6cedf150c5a3ddf/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:50bff43b632a56fbf5ed9afdd76307e1512b62051bcd5afb341ae67205bbb6c8", size = 3268125, upload-time = "2026-08-11T21:02:47.649Z" },
{ url = "https://files.pythonhosted.org/packages/35/f3/ea8933fc9f7d1353e9c2ff9965eae687c4cef181120574591ed2fa0633e1/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:49565daf5af554f538e23aef1fc81a95a4e49658f152285e45c02f5fc44f04cd", size = 3289516, upload-time = "2026-08-11T21:17:07.267Z" },
{ url = "https://files.pythonhosted.org/packages/45/67/05cf86541c1e1716fca1e4a996954a439cd74501707cda607fb7cb02ef50/sqlalchemy-2.0.52-cp314-cp314-win32.whl", hash = "sha256:ab9da41e61b9979b910499d633b241df20c51ee5037e5405b11c2faac3cbe1a2", size = 2130249, upload-time = "2026-08-11T21:14:57.273Z" },
{ url = "https://files.pythonhosted.org/packages/96/d7/8ac6ffa1e36169e762ef65bd835046abb2251b1bc17f8f6708e14ed8d31f/sqlalchemy-2.0.52-cp314-cp314-win_amd64.whl", hash = "sha256:a593db51b3bae75db17a5738ad5f992244b3a03863f83c28117ee482c6a3f76d", size = 2156718, upload-time = "2026-08-11T21:14:58.667Z" },
{ url = "https://files.pythonhosted.org/packages/dc/4b/e01a737eef378e734cc6394a82248a6ce13b167dfa36c731075ce9fc9c64/sqlalchemy-2.0.52-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1e61d08bdf4ee2f41024569e3400de7d6734ba498144766b11260936ccfa582", size = 2190344, upload-time = "2026-08-11T19:53:21.393Z" },
{ url = "https://files.pythonhosted.org/packages/b3/3f/3582293d1e185e71d19d7c731c3e2ee20ba21981c4a1115c0806c1f62120/sqlalchemy-2.0.52-py3-none-any.whl", hash = "sha256:3b81b8363a919ce53453591cdb93702e6bd54ade6c4fa2f468fc053baee5ed89", size = 1950700, upload-time = "2026-08-11T20:47:21.603Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2eb3c6a64b1bfe6704777cfd504e7b8ad093a5f3e03ce67663a5e6742f294e43", size = 2167724, upload-time = "2026-08-11T20:58:12.679Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:923bb183c1dc64fdf7b717965e3d59938ec4f8b8710b419a21ce403e5da9a9e1", size = 3321189, upload-time = "2026-08-11T21:02:41.932Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:651d6d8782e80679e6151707c7b490834d46ada526328895abf567f25e63d29c", size = 3338185, upload-time = "2026-08-11T21:17:02.597Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b08cddb8989775e3c88799d86704bdfc3ee6e9846118201aa5997f16f27e3a15", size = 3271698, upload-time = "2026-08-11T21:02:43.963Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab66fa9618269390d4dfa222f2f2f88f7bc4bf5da13905131b818217db7e8057", size = 3308936, upload-time = "2026-08-11T21:17:04.172Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-win32.whl", hash = "sha256:c63bda077685c85ca513286547a531ba57e7a68cf0a7ed3bafcc2bbd18896f4d", size = 2127308, upload-time = "2026-08-11T21:14:53.879Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp313-cp313-win_amd64.whl", hash = "sha256:9876b09b9f1ce7398b0ffece585c0a911244c53191187341f6bcae640e133751", size = 2153876, upload-time = "2026-08-11T21:14:55.527Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:410d52be41d17f1a236d19520fbe776257dc16516ed06bd16d433311842aefd9", size = 2169537, upload-time = "2026-08-11T20:58:13.855Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfe9ce533dbe4d0a2ae1486546619bd30b76bcd670539a44d910361376175f5e", size = 3319606, upload-time = "2026-08-11T21:02:45.829Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:812bae5138bfc0aa46fb0686da0fc7f581f68e2bbb05bc24c3713bebaedd1437", size = 3323642, upload-time = "2026-08-11T21:17:05.675Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:50bff43b632a56fbf5ed9afdd76307e1512b62051bcd5afb341ae67205bbb6c8", size = 3268125, upload-time = "2026-08-11T21:02:47.649Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:49565daf5af554f538e23aef1fc81a95a4e49658f152285e45c02f5fc44f04cd", size = 3289516, upload-time = "2026-08-11T21:17:07.267Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-win32.whl", hash = "sha256:ab9da41e61b9979b910499d633b241df20c51ee5037e5405b11c2faac3cbe1a2", size = 2130249, upload-time = "2026-08-11T21:14:57.273Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314-win_amd64.whl", hash = "sha256:a593db51b3bae75db17a5738ad5f992244b3a03863f83c28117ee482c6a3f76d", size = 2156718, upload-time = "2026-08-11T21:14:58.667Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1e61d08bdf4ee2f41024569e3400de7d6734ba498144766b11260936ccfa582", size = 2190344, upload-time = "2026-08-11T19:53:21.393Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/sqlalchemy/2.0.52/sqlalchemy-2.0.52-py3-none-any.whl", hash = "sha256:3b81b8363a919ce53453591cdb93702e6bd54ade6c4fa2f468fc053baee5ed89", size = 1950700, upload-time = "2026-08-11T20:47:21.603Z" },
]
[[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" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/tomli-w/1.2.0/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/tomli-w/1.2.0/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" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/typing-extensions/4.16.0/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" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/typing-extensions/4.16.0/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.4"
source = { registry = "https://pypi.org/simple" }
source = { registry = "https://nexus.thpeetz.de/repository/pypi-proxy/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" }
sdist = { url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/typing-inspection/0.4.4/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" },
{ url = "https://nexus.thpeetz.de/repository/pypi-proxy/packages/typing-inspection/0.4.4/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" },
]
@@ -8,7 +8,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag
@OpenAPIDefinition(
info = Info(
title = "Kontor",
version = "0.3.0"
version = "0.4.0-SNAPSHOT"
)
)
class KontorApplication: Application()
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "kontor-robyn"
version = "0.1.0"
version = "0.4.0"
description = "Add your description here"
readme = "README.md"
authors = [
+10 -4
View File
@@ -187,10 +187,16 @@ class Server:
url, headers=headers, json=new_item, timeout=self.timeout
)
log.info(f"Status: {create.status_code}")
if create.status_code == 404:
raise EndPointNotAvailableException
if create.status_code == 409:
log.fatal("Create Exception %s", create.json())
match create.status_code:
case 404:
log.info("Endpoint %s not available", url)
raise EndPointNotAvailableException
case 405:
log.info("POST request %s returned 405", url)
case 409:
log.fatal("Create Exception %s", create.json())
case _:
log.info("")
data = create.json()
return data
+9
View File
@@ -139,6 +139,8 @@ def item_delete(table_name: str, item_id: str, api_data: Dict[str, Any], log: Lo
url = f"http://{host}:{port}/api/media/actorfiles/{item_id}"
case "media_actor":
url = f"http://{host}:{port}/api/media/actors/{item_id}"
case "media_lofi":
url = f"http://{host}:{port}/api/media/lofi/{item_id}"
case "profile":
url = f"http://{host}:{port}/api/user/profile/{item_id}"
headers: Dict[str, str] = {"Authorization": f"Bearer {token}"}
@@ -209,6 +211,13 @@ if __name__ == "__main__":
api_data=api_data,
log=logger
)
case "media_lofi":
item_delete(
table_name=tablename,
item_id=item_id,
api_data=api_data,
log=logger
)
case _:
logger.info("Method to remove remaining item not implemented")
logger.info("kontor.import finished")
+12 -3
View File
@@ -1,7 +1,7 @@
[project]
requires-python = ">=3.13"
name = "kontor-scripts"
version = "0.1.0"
version = "0.4.0"
readme = "README.md"
authors = [
{name = "Thomas Peetz", email = "thomas.peetz@thpeetz.de"}
@@ -28,7 +28,16 @@ dependencies = [
"sqlalchemy>=2.0.40",
"sqlmodel>=0.0.24",
"stomp-py",
"kontor-model",
]
[tool.uv.sources]
kontor_model = { path = "../kontor-model", editable = true }
[[tool.uv.index]]
name = "nexus"
url = "https://nexus.thpeetz.de/repository/pypi-group/simple"
publish-url = "https://nexus.thpeetz.de/repository/pypi-internal/"
default = true
[[tool.uv.index]]
name = "nexus-proxy"
url = "https://nexus.thpeetz.de/repository/pypi-proxy/simple"
+404 -409
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@ FROM docker.io/alpine/java:21-jdk AS run
RUN mkdir -p /logs
COPY --from=builder /build/libs/kontor-spring-0.3.0.jar app.jar
COPY --from=builder /build/libs/kontor-spring-0.4.0-SNAPSHOT.jar app.jar
EXPOSE 8100
+1 -1
View File
@@ -2,7 +2,7 @@ FROM docker.io/alpine/java:21-jdk AS run
RUN mkdir -p /logs
COPY ./build/libs/kontor-spring-0.3.0.jar app.jar
COPY ./build/libs/kontor-spring-0.4.0-SNAPSHOT.jar app.jar
EXPOSE 8100
+8 -16
View File
@@ -2,7 +2,6 @@ plugins {
id 'java'
id 'application'
id 'maven-publish'
//id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.0"
id 'jvm-test-suite'
id 'jacoco'
id 'test-report-aggregation'
@@ -11,13 +10,11 @@ plugins {
alias(libs.plugins.spring.dependencies)
alias(libs.plugins.vaadin)
alias(libs.plugins.lombok)
//id 'com.github.ksoichiro.build.info' version '0.2.0'
//id 'com.pasam.gradle.buildinfo' version '0.1.3'
id 'com.gorylenko.gradle-git-properties' version '4.0.1'
alias(libs.plugins.gradle.git.properties)
}
repositories {
maven { setUrl("https://nexus.thpeetz.de/repository/maven-central") }
maven { setUrl("https://nexus.thpeetz.de/repository/maven-public") }
mavenCentral()
maven { setUrl("https://maven.vaadin.com/vaadin-prereleases") }
maven { setUrl("https://repo.spring.io/milestone") }
@@ -25,7 +22,7 @@ repositories {
}
java {
sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
}
configurations {
@@ -38,23 +35,23 @@ configurations {
dependencies {
implementation 'com.vaadin:vaadin-core'
implementation 'com.vaadin:vaadin-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-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.apache.camel.springboot:camel-spring-boot-starter'
implementation 'org.springframework.security:spring-security-oauth2-jose'
implementation 'org.springframework.security:spring-security-oauth2-resource-server'
implementation 'org.apache.camel.springboot:camel-jms-starter'
implementation 'org.apache.camel.springboot:camel-metrics-starter'
implementation 'org.apache.camel.springboot:camel-micrometer-starter'
implementation 'org.apache.camel.springboot:camel-spring-boot-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.jolokia.core
implementation libs.prometheus.collector
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'
@@ -149,7 +146,6 @@ testing {
}
}
test(JvmTestSuite) {
testType = TestSuiteType.UNIT_TEST
targets {
all {
testTask.configure {
@@ -165,7 +161,6 @@ testing {
}
}
integrationTest(JvmTestSuite) {
testType = "view-test"
targets {
all {
testTask.configure {
@@ -197,13 +192,10 @@ jacocoTestReport {
reporting {
reports {
testAggregateTestReport(AggregateTestReport) {
testType = TestSuiteType.UNIT_TEST
}
integrationTestAggregateTestReport(AggregateTestReport) {
testType = "view-test"
}
integrationTestCodeCoverageReport(JacocoCoverageReport) {
testType = "view-test"
}
}
}
@@ -1,3 +0,0 @@
{
"lumoImports" : [ "typography", "color", "spacing", "badge", "utility" ]
}
+1 -1
View File
@@ -1,5 +1,5 @@
description='Kontor with Spring Boot'
version=0.3.0
version=0.4.0-SNAPSHOT
group=de.thpeetz
nexusUser=kontor
nexusPassword=kontorNexus
+15 -21
View File
@@ -1,39 +1,32 @@
[versions]
gradle = "8.6"
args4j = "2.33"
commonscli = "1.5.0"
junit = "5.8.2"
logback = "1.6.3"
mockito = "1.9.5"
picoli = "4.7.0"
slf4j = "2.0.18"
hsqldb = "2.7.1"
sqlite = "3.25.2"
spotbugs = "6.0.7"
sonarqube = "3.3"
springboot = "3.2.5"
springdependencies = "1.1.4"
vaadin = "24.4.23"
gradle = "9.5.1"
springboot = "4.1.0"
springdependencies = "1.1.7"
vaadin = "25.2.6"
camel = "4.10.6"
artemis = "2.41.0"
logback = "1.6.3"
lombok = "8.11"
gson = "2.9.0"
jackson = "2.16.1"
json_simple = "1.1.1"
jsoup = "1.23.1"
mail = "1.6.2"
hypersistence = "3.9.10"
hypersistence = "3.15.4"
jolokia = "2.6.1"
prometheus = "1.6.0"
slf4j = "2.0.18"
junit = "5.8.2"
hsqldb = "2.7.1"
sqlite = "3.25.2"
spotbugs = "6.0.7"
sonarqube = "3.3"
gradleGitProperties = "4.0.1"
[libraries]
args4j = { module = "args4j:args4j", version.ref = "args4j" }
commonscli = { module = "commons-cli:commons-cli", version.ref = "commonscli" }
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" }
@@ -42,7 +35,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" }
hypersistence = { module = "io.hypersistence:hypersistence-utils-hibernate-73", 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" }
@@ -59,3 +52,4 @@ 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" }
lombok = { id = "io.freefair.lombok", version.ref = "lombok" }
gradle-git-properties = { id = "com.gorylenko.gradle-git-properties", version.ref = "gradleGitProperties" }
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%
Binary file not shown.
@@ -0,0 +1 @@
export {}
@@ -0,0 +1 @@
export declare const applyCss: (target: Node) => void;
@@ -0,0 +1,52 @@
import { injectGlobalCss } from 'Frontend/generated/jar-resources/theme-util.js';
import { webcomponentGlobalCssInjector } from 'Frontend/generated/jar-resources/theme-util.js';
let needsReloadOnChanges = false;
let themeRemovers = new WeakMap();
let targets = [];
const fontFaceRegex = /(@font-face\s*{[\s\S]*?})/g;
export const applyCss = (target) => {
const removers = [];
if (target !== document) {
webcomponentGlobalCssInjector((css) => {
removers.push(injectGlobalCss(css, '', target));
if(fontFaceRegex.test(css)) {
const fontFaces = Array.from(css.match(fontFaceRegex));
fontFaces.forEach(fontFace => {
removers.push(injectGlobalCss(fontFace, '', document));
});
}
});
}
if (import.meta.hot) {
targets.push(new WeakRef(target));
themeRemovers.set(target, removers);
}
}
if (import.meta.hot) {
import.meta.hot.accept((module) => {
if (needsReloadOnChanges) {
window.location.reload();
} else {
targets.forEach(targetRef => {
const target = targetRef.deref();
if (target) {
themeRemovers.get(target).forEach(remover => remover())
module.applyTheme(target);
}
})
}
});
import.meta.hot.on('vite:afterUpdate', (update) => {
document.dispatchEvent(new CustomEvent('vaadin-theme-updated', { detail: update }));
});
}
@@ -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,43 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/custom-field/src/vaadin-custom-field.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.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';
@@ -0,0 +1,43 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
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/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
@@ -0,0 +1,41 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
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/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
@@ -0,0 +1,41 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
@@ -0,0 +1,46 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
import '@vaadin/integer-field/src/vaadin-integer-field.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
import '@vaadin/custom-field/src/vaadin-custom-field.js';
@@ -0,0 +1,42 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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/password-field/src/vaadin-password-field.js';
import '@vaadin/email-field/src/vaadin-email-field.js';
import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js';
@@ -0,0 +1,40 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
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';
@@ -0,0 +1,48 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
import '@vaadin/integer-field/src/vaadin-integer-field.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
import '@vaadin/custom-field/src/vaadin-custom-field.js';
import '@vaadin/select/src/vaadin-select.js';
import 'Frontend/generated/jar-resources/selectConnector.js';
@@ -0,0 +1,44 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
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/integer-field/src/vaadin-integer-field.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
@@ -0,0 +1,18 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
@@ -0,0 +1,30 @@
import '@vaadin/avatar/src/vaadin-avatar.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.js';
@@ -0,0 +1,39 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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';
@@ -0,0 +1,45 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.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/grid/src/vaadin-grid-column-group.js';
import 'Frontend/generated/jar-resources/lit-renderer.ts';
import '@vaadin/text-field/src/vaadin-text-field.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/list-box/src/vaadin-list-box.js';
import '@vaadin/item/src/vaadin-item.js';
import '@vaadin/integer-field/src/vaadin-integer-field.js';
import '@vaadin/combo-box/src/vaadin-combo-box.js';
import 'Frontend/generated/jar-resources/comboBoxConnector.js';
import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js';
@@ -0,0 +1,28 @@
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
@@ -0,0 +1,29 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/app-layout/src/vaadin-app-layout.js';
import '@vaadin/scroller/src/vaadin-scroller.js';
import '@vaadin/side-nav/src/vaadin-side-nav.js';
import '@vaadin/side-nav/src/vaadin-side-nav-item.js';
import '@vaadin/tooltip/src/vaadin-tooltip.js';
import '@vaadin/app-layout/src/vaadin-drawer-toggle.js';
import '@vaadin/button/src/vaadin-button.js';
import 'Frontend/generated/jar-resources/disableOnClickFunctions.js';
import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js';
import '@vaadin/icons/vaadin-iconset.js';
import '@vaadin/icon/src/vaadin-icon.js';
import '@vaadin/upload/src/vaadin-upload.js';
import 'Frontend/generated/jar-resources/vaadin-upload-manager-connector.ts';
import '@vaadin/notification/src/vaadin-notification.js';
import 'Frontend/generated/jar-resources/flow-component-renderer.js';
import 'Frontend/generated/jar-resources/flow-component-directive.js';
import '@vaadin/checkbox/src/vaadin-checkbox.js';
import 'Frontend/generated/jar-resources/menubarConnector.js';
import 'Frontend/generated/jar-resources/contextMenuConnector.js';
import '@vaadin/menu-bar/src/vaadin-menu-bar.js';
import '@vaadin/context-menu/src/vaadin-context-menu.js';
import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js';
import '@vaadin/accordion/src/vaadin-accordion.js';
import '@vaadin/accordion/src/vaadin-accordion-panel.js';
import '@vaadin/details/src/vaadin-details.js';
import 'Frontend/generated/jar-resources/messageListConnector.js';
import '@vaadin/message-list/src/vaadin-message-list.js';
import '@vaadin/text-field/src/vaadin-text-field.js';
@@ -0,0 +1 @@
export {}
@@ -0,0 +1,126 @@
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/login/src/vaadin-login-form.js';
import '@vaadin/common-frontend/ConnectionIndicator.js';
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
const loadOnDemand = (key) => {
const pending = [];
if (key === '81bbe2e11b6af26d0d292e2e18bde37546d5b9ec24f4f4f78df2bc9f9c21f431') {
pending.push(import('./chunks/chunk-ba68801837253f4860e3f4374e9ae9e48ccba4ca6110cac47de37e40d7b6199e.js'));
}
if (key === '8904207b9d538b9eedfaaefd8e82d3415de8513bda9d6bb76f4012f0f336d200') {
pending.push(import('./chunks/chunk-34d02800587f0d3950a0cf1f566793c706accc702ca93d2f51ab25f74d29bd84.js'));
}
if (key === '96ecac798fbbe91ef146ac4a25f5c31827427ca0f106ba66e63fc1fd6c27b83d') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'c0816c2bfaa97d6f3e7eea83c5478186457bdc79cfe3f284c42e192d68607157') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '58cc18f5518623050c6a99d327dd540140b9b0dca851a76a70e688291f661e11') {
pending.push(import('./chunks/chunk-94486ac490d7aebdfb2696122da529dc74cba54f50b75f3f737368bd86850d36.js'));
}
if (key === '83045a9760ea76fd534ed71ed553dc7cf2595a5d697e3c7bc55470d219770d05') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'e9df10b3be58513bb6452e4dbdce31e0b1dc33868fbba54ee72e82089cf39adf') {
pending.push(import('./chunks/chunk-34d02800587f0d3950a0cf1f566793c706accc702ca93d2f51ab25f74d29bd84.js'));
}
if (key === '39b4a07b2b07a780f8ac9d2f7cc855e3b78ec60465d9d808690466c1f0bea792') {
pending.push(import('./chunks/chunk-9067a52cbc57d972c6155501cef5bd27a816f0c7adf6390a603c7b4e9bbddce3.js'));
}
if (key === '803ed44611ffeb1bcb1f604f267f09af48ef8a5bc8d765e481b67ed59f9b880a') {
pending.push(import('./chunks/chunk-5f54f3be0c0d908c0477e60450f72fbb60bfa31e84e7d18b39841a003d995f86.js'));
}
if (key === '9f40817b690e11a5b23a3159bb774f15430c23768f26dc0d15efccc3d65223ac') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '391ac9fb4bd95bc899c5911c48a6f3b549b118923653444f5ab50acddfb6f291') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '1ac8bd8d734d2603947b1db0616ea4dae4ce691a5264f5c9aeafcaa897e0a872') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'e5db1fd1f39047ab2857e9ef118681c3f831f39efbbb2a6f000e9475b77444e5') {
pending.push(import('./chunks/chunk-94486ac490d7aebdfb2696122da529dc74cba54f50b75f3f737368bd86850d36.js'));
}
if (key === '92a567041312ea4c7faa4dd5bcc74f73cd3b16dd721624d0bc68d1704138abe5') {
pending.push(import('./chunks/chunk-4f16ee624545500a5dde991be4f07a661d86951bbc2c07a010daabbd423b1909.js'));
}
if (key === 'bfac19b5aab9cd5a47ff4a66f2a839ebbb4889867824be809c5dc3a3d47f94b8') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'a886fd99e05dd2c5ce3e4b643dc8d6d29189dee784fa78678d8ab4fcb89216c0') {
pending.push(import('./chunks/chunk-4643e5b30878e982c301927c78b86af508e91d91560341ef83fa0e6bf79dedee.js'));
}
if (key === 'becc589ccd4f7ac5e6f13c45eecc05f61d67ce57f585f1655800354ff4b55f3c') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '0ca48a0e12f1dbf4945f761a93a7df202b2908327a63c52f2f060559c2bcc7f0') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2140ca6af7e9a5ff94d8a66b6e5a23e6d184ef7901ca73d33b3c30f74f94708a') {
pending.push(import('./chunks/chunk-eab035454a058f437d12e5ccf64ef647181029b965d466c829f5a569b2865dd3.js'));
}
if (key === '89c2914ed5d19860240c0c1d3dba174616dc452c4ba8ae039f016cf9592e9432') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'ffd87f879398d3b6942e852eda9b64e28a124c8a61938c867efb360a5cf1d32e') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'c5f987a4896c791d13aeb6a03a4783ff03df304750a7db930a18d399dba31509') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '3665d7598c9ed4112997a0493fb890edf38d23d3bd9fcb0eb09bf3e045f5228b') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '4e76ad4411fbf58731cdad95d28e6fe0057fb4a97ffd54f193e52b111462552d') {
pending.push(import('./chunks/chunk-ba68801837253f4860e3f4374e9ae9e48ccba4ca6110cac47de37e40d7b6199e.js'));
}
if (key === '3e87cbb0d40d50da9c3b69d0dada85fe9b7f2f4bae2b79c8f0408a798b120549') {
pending.push(import('./chunks/chunk-5e457c2d02d56b468106fc6b3ad37f1215d95af443e88a25314d583049fa7ada.js'));
}
if (key === 'ac7af13a0805d3ce8a287e9d860521c82aaf55d2c448ce3f62a85d44216a8189') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'a4eeeab53770801e478b15e6b179e5689afd43fc753f347e0c51ce934bd28a6c') {
pending.push(import('./chunks/chunk-0980a344d761e100f78a8df513a682bdcc702314eb249c64b972cbe64fc2a813.js'));
}
if (key === 'dca37e6961f625182e78a722a61c0eb9b8a2357b5e787fe616fe5993c2db5133') {
pending.push(import('./chunks/chunk-dbbf8c73fe9c2c76fb9306f96bfa7ff293eff804705670e651ac024bea9551bd.js'));
}
if (key === '5d710852587ce9ebabfac9ed950f6b11bc3a87d96860d4efdfb61dab7ae54091') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2b0ca0e51ded6027d3f9e464513210d5503ab2a9bc532de5cfc81d78a58f422f') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '28b5e9145505152a4dbd9600977b8177d3a161906581ca5a1a923affc812a761') {
pending.push(import('./chunks/chunk-5e457c2d02d56b468106fc6b3ad37f1215d95af443e88a25314d583049fa7ada.js'));
}
if (key === '68a5f871903ca703f0fac8dd0072e1bfc082ffa14aa445026be6e1eb6cfe1bd0') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '53ff1588255d7e5486eb67452c8d9c43baea0eecd9bd509fd7f0c55b3cccaa0d') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '4e356c0939a28ab8766b0375ca6cb478b6163eb9ad80745a9e2f2fc80997b6a2') {
pending.push(import('./chunks/chunk-82d1654bda3835e43a2a0c9a2e52925f12dc4a2fcd20b5f40603dbbe6eb1ff23.js'));
}
if (key === 'b4afd0c0906e26b51836d41720e1b8d91a7cacf0fd42e089f1e0fbc2ee27a5bb') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2062a3f35e8fdb051e9362a8a8895d80ef719832e9394babcbaab3aac56d4f0e') {
pending.push(import('./chunks/chunk-0980a344d761e100f78a8df513a682bdcc702314eb249c64b972cbe64fc2a813.js'));
}
return Promise.all(pending);
}
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,127 @@
import { injectGlobalWebcomponentCss } from 'Frontend/generated/jar-resources/theme-util.js';
import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js';
import '@vaadin/login/src/vaadin-login-form.js';
import '@vaadin/common-frontend/ConnectionIndicator.js';
import 'Frontend/generated/jar-resources/ReactRouterOutletElement.tsx';
const loadOnDemand = (key) => {
const pending = [];
if (key === '81bbe2e11b6af26d0d292e2e18bde37546d5b9ec24f4f4f78df2bc9f9c21f431') {
pending.push(import('./chunks/chunk-ba68801837253f4860e3f4374e9ae9e48ccba4ca6110cac47de37e40d7b6199e.js'));
}
if (key === '8904207b9d538b9eedfaaefd8e82d3415de8513bda9d6bb76f4012f0f336d200') {
pending.push(import('./chunks/chunk-34d02800587f0d3950a0cf1f566793c706accc702ca93d2f51ab25f74d29bd84.js'));
}
if (key === '96ecac798fbbe91ef146ac4a25f5c31827427ca0f106ba66e63fc1fd6c27b83d') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'c0816c2bfaa97d6f3e7eea83c5478186457bdc79cfe3f284c42e192d68607157') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '58cc18f5518623050c6a99d327dd540140b9b0dca851a76a70e688291f661e11') {
pending.push(import('./chunks/chunk-94486ac490d7aebdfb2696122da529dc74cba54f50b75f3f737368bd86850d36.js'));
}
if (key === '83045a9760ea76fd534ed71ed553dc7cf2595a5d697e3c7bc55470d219770d05') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'e9df10b3be58513bb6452e4dbdce31e0b1dc33868fbba54ee72e82089cf39adf') {
pending.push(import('./chunks/chunk-34d02800587f0d3950a0cf1f566793c706accc702ca93d2f51ab25f74d29bd84.js'));
}
if (key === '39b4a07b2b07a780f8ac9d2f7cc855e3b78ec60465d9d808690466c1f0bea792') {
pending.push(import('./chunks/chunk-9067a52cbc57d972c6155501cef5bd27a816f0c7adf6390a603c7b4e9bbddce3.js'));
}
if (key === '803ed44611ffeb1bcb1f604f267f09af48ef8a5bc8d765e481b67ed59f9b880a') {
pending.push(import('./chunks/chunk-5f54f3be0c0d908c0477e60450f72fbb60bfa31e84e7d18b39841a003d995f86.js'));
}
if (key === '9f40817b690e11a5b23a3159bb774f15430c23768f26dc0d15efccc3d65223ac') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '391ac9fb4bd95bc899c5911c48a6f3b549b118923653444f5ab50acddfb6f291') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '1ac8bd8d734d2603947b1db0616ea4dae4ce691a5264f5c9aeafcaa897e0a872') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === 'e5db1fd1f39047ab2857e9ef118681c3f831f39efbbb2a6f000e9475b77444e5') {
pending.push(import('./chunks/chunk-94486ac490d7aebdfb2696122da529dc74cba54f50b75f3f737368bd86850d36.js'));
}
if (key === '92a567041312ea4c7faa4dd5bcc74f73cd3b16dd721624d0bc68d1704138abe5') {
pending.push(import('./chunks/chunk-4f16ee624545500a5dde991be4f07a661d86951bbc2c07a010daabbd423b1909.js'));
}
if (key === 'bfac19b5aab9cd5a47ff4a66f2a839ebbb4889867824be809c5dc3a3d47f94b8') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'a886fd99e05dd2c5ce3e4b643dc8d6d29189dee784fa78678d8ab4fcb89216c0') {
pending.push(import('./chunks/chunk-4643e5b30878e982c301927c78b86af508e91d91560341ef83fa0e6bf79dedee.js'));
}
if (key === 'becc589ccd4f7ac5e6f13c45eecc05f61d67ce57f585f1655800354ff4b55f3c') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '0ca48a0e12f1dbf4945f761a93a7df202b2908327a63c52f2f060559c2bcc7f0') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2140ca6af7e9a5ff94d8a66b6e5a23e6d184ef7901ca73d33b3c30f74f94708a') {
pending.push(import('./chunks/chunk-eab035454a058f437d12e5ccf64ef647181029b965d466c829f5a569b2865dd3.js'));
}
if (key === '89c2914ed5d19860240c0c1d3dba174616dc452c4ba8ae039f016cf9592e9432') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'ffd87f879398d3b6942e852eda9b64e28a124c8a61938c867efb360a5cf1d32e') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'c5f987a4896c791d13aeb6a03a4783ff03df304750a7db930a18d399dba31509') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '3665d7598c9ed4112997a0493fb890edf38d23d3bd9fcb0eb09bf3e045f5228b') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === '4e76ad4411fbf58731cdad95d28e6fe0057fb4a97ffd54f193e52b111462552d') {
pending.push(import('./chunks/chunk-ba68801837253f4860e3f4374e9ae9e48ccba4ca6110cac47de37e40d7b6199e.js'));
}
if (key === '3e87cbb0d40d50da9c3b69d0dada85fe9b7f2f4bae2b79c8f0408a798b120549') {
pending.push(import('./chunks/chunk-5e457c2d02d56b468106fc6b3ad37f1215d95af443e88a25314d583049fa7ada.js'));
}
if (key === 'ac7af13a0805d3ce8a287e9d860521c82aaf55d2c448ce3f62a85d44216a8189') {
pending.push(import('./chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js'));
}
if (key === 'a4eeeab53770801e478b15e6b179e5689afd43fc753f347e0c51ce934bd28a6c') {
pending.push(import('./chunks/chunk-0980a344d761e100f78a8df513a682bdcc702314eb249c64b972cbe64fc2a813.js'));
}
if (key === 'dca37e6961f625182e78a722a61c0eb9b8a2357b5e787fe616fe5993c2db5133') {
pending.push(import('./chunks/chunk-dbbf8c73fe9c2c76fb9306f96bfa7ff293eff804705670e651ac024bea9551bd.js'));
}
if (key === '5d710852587ce9ebabfac9ed950f6b11bc3a87d96860d4efdfb61dab7ae54091') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2b0ca0e51ded6027d3f9e464513210d5503ab2a9bc532de5cfc81d78a58f422f') {
pending.push(import('./chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js'));
}
if (key === '28b5e9145505152a4dbd9600977b8177d3a161906581ca5a1a923affc812a761') {
pending.push(import('./chunks/chunk-5e457c2d02d56b468106fc6b3ad37f1215d95af443e88a25314d583049fa7ada.js'));
}
if (key === '68a5f871903ca703f0fac8dd0072e1bfc082ffa14aa445026be6e1eb6cfe1bd0') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '53ff1588255d7e5486eb67452c8d9c43baea0eecd9bd509fd7f0c55b3cccaa0d') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '4e356c0939a28ab8766b0375ca6cb478b6163eb9ad80745a9e2f2fc80997b6a2') {
pending.push(import('./chunks/chunk-82d1654bda3835e43a2a0c9a2e52925f12dc4a2fcd20b5f40603dbbe6eb1ff23.js'));
}
if (key === 'b4afd0c0906e26b51836d41720e1b8d91a7cacf0fd42e089f1e0fbc2ee27a5bb') {
pending.push(import('./chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js'));
}
if (key === '2062a3f35e8fdb051e9362a8a8895d80ef719832e9394babcbaab3aac56d4f0e') {
pending.push(import('./chunks/chunk-0980a344d761e100f78a8df513a682bdcc702314eb249c64b972cbe64fc2a813.js'));
}
return Promise.all(pending);
}
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,94 @@
app-shell-imports.d.ts
app-shell-imports.js
css.generated.d.ts
flow/Flow.tsx
flow/ReactAdapter.tsx
flow/chunks/chunk-0980a344d761e100f78a8df513a682bdcc702314eb249c64b972cbe64fc2a813.js
flow/chunks/chunk-34d02800587f0d3950a0cf1f566793c706accc702ca93d2f51ab25f74d29bd84.js
flow/chunks/chunk-4643e5b30878e982c301927c78b86af508e91d91560341ef83fa0e6bf79dedee.js
flow/chunks/chunk-4f16ee624545500a5dde991be4f07a661d86951bbc2c07a010daabbd423b1909.js
flow/chunks/chunk-5e457c2d02d56b468106fc6b3ad37f1215d95af443e88a25314d583049fa7ada.js
flow/chunks/chunk-5f54f3be0c0d908c0477e60450f72fbb60bfa31e84e7d18b39841a003d995f86.js
flow/chunks/chunk-79565f1f0670029d94da9f00948c9fc73575b3c6dc12ea07cca72303e68fbd57.js
flow/chunks/chunk-7eb0e92a81bb7e9cb5e0c25a28ae3459e5223c6322ff3569292a66f45a9990ae.js
flow/chunks/chunk-82d1654bda3835e43a2a0c9a2e52925f12dc4a2fcd20b5f40603dbbe6eb1ff23.js
flow/chunks/chunk-9067a52cbc57d972c6155501cef5bd27a816f0c7adf6390a603c7b4e9bbddce3.js
flow/chunks/chunk-94486ac490d7aebdfb2696122da529dc74cba54f50b75f3f737368bd86850d36.js
flow/chunks/chunk-ba68801837253f4860e3f4374e9ae9e48ccba4ca6110cac47de37e40d7b6199e.js
flow/chunks/chunk-c2a7e008275ccefd2b71f714af29d0186f49edb012ac8081cb81d0a896594da6.js
flow/chunks/chunk-dbbf8c73fe9c2c76fb9306f96bfa7ff293eff804705670e651ac024bea9551bd.js
flow/chunks/chunk-eab035454a058f437d12e5ccf64ef647181029b965d466c829f5a569b2865dd3.js
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/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-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"]}
@@ -0,0 +1,17 @@
import { Outlet } from 'react-router';
import { ReactAdapterElement } from "Frontend/generated/flow/ReactAdapter.js";
import React from "react";
class ReactRouterOutletElement extends ReactAdapterElement {
public async connectedCallback() {
await super.connectedCallback();
this.style.display = 'contents';
}
protected render(): React.ReactElement | null {
return <Outlet />;
}
}
customElements.define('react-router-outlet', ReactRouterOutletElement);
@@ -0,0 +1,12 @@
/**
* Returns the current screen orientation type synchronously, or
* {@code 'unsupported'} if the Screen Orientation API is unavailable. Used by
* the bootstrap path to seed the server-side signal without waiting for a DOM
* event.
*/
export declare function currentScreenOrientationType(): string;
/**
* Returns the current screen orientation angle synchronously, or 0 if the
* Screen Orientation API is unavailable.
*/
export declare function currentScreenOrientationAngle(): number;
@@ -0,0 +1,92 @@
/*
* 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 screen orientation type synchronously, or
* {@code 'unsupported'} if the Screen Orientation API is unavailable. Used by
* the bootstrap path to seed the server-side signal without waiting for a DOM
* event.
*/
export function currentScreenOrientationType() {
return screen.orientation?.type ?? 'unsupported';
}
/**
* Returns the current screen orientation angle synchronously, or 0 if the
* Screen Orientation API is unavailable.
*/
export function currentScreenOrientationAngle() {
return screen.orientation?.angle ?? 0;
}
// Dispatch on document.body so the server-side ScreenOrientation facade
// (listening on the UI element, which is body) can update its signal.
function dispatch(detail) {
document.body.dispatchEvent(new CustomEvent('vaadin-screen-orientation-change', { detail }));
}
if (screen.orientation) {
screen.orientation.addEventListener('change', () => {
dispatch({
type: screen.orientation.type,
angle: screen.orientation.angle
});
});
}
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
function lockErrorCode(domExceptionName) {
switch (domExceptionName) {
case 'NotSupportedError':
return 'NOT_SUPPORTED';
case 'SecurityError':
return 'SECURITY';
case 'AbortError':
return 'ABORT';
default:
return 'UNKNOWN';
}
}
$wnd.Vaadin.Flow.screenOrientation = {
// Always resolves so the server-side .then(success, error) chain only
// receives the "error" branch on a bridge failure (lost connection, etc.).
// Rejected DOMExceptions are folded into the resolved result so the server
// can decode them as a record without forfeiting the JS-bridge error arm.
lock(type) {
if (!screen.orientation || typeof screen.orientation.lock !== 'function') {
return Promise.resolve({
success: false,
code: 'NOT_SUPPORTED',
message: 'Screen Orientation API is not supported in this browser.'
});
}
return screen.orientation
.lock(type)
.then(() => ({ success: true }))
.catch((e) => {
const code = lockErrorCode(e.name);
const message = e.message ?? '';
return {
success: false,
code,
// The DOMException name is dropped once mapped to a typed code;
// keep it in the message for diagnostics when no code matches.
message: code === 'UNKNOWN' && e.name ? `${e.name}: ${message}` : message
};
});
},
unlock() {
screen.orientation?.unlock();
}
};
//# sourceMappingURL=ScreenOrientation.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,120 @@
/*
* 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.
*/
// Whether the server-side has asked us to hold the lock. The browser releases
// the lock whenever the tab is hidden; this flag is what lets the
// visibilitychange handler re-acquire silently when the tab returns.
let wanted = false;
let sentinel = null;
let visibilityListenerInstalled = false;
function dispatch(element, state) {
element.dispatchEvent(new CustomEvent('vaadin-wake-lock-change', { detail: state }));
}
async function acquire(element) {
if (sentinel) {
return { state: 'granted' };
}
if (!window.isSecureContext || !('wakeLock' in navigator)) {
return {
state: 'error',
errorCode: 'UNSUPPORTED',
message: window.isSecureContext
? 'Screen Wake Lock API not implemented in this browser'
: 'Screen Wake Lock API requires a secure context (HTTPS or localhost)'
};
}
try {
const next = await navigator.wakeLock.request('screen');
// The user (or the browser) may have released the lock or the tab may have
// been hidden again while the request was in flight.
if (!wanted || document.visibilityState !== 'visible') {
try {
await next.release();
}
catch (_e) {
// Ignore; releasing an already-released sentinel throws on some
// browsers and there is nothing meaningful to do here.
}
return { state: 'deferred' };
}
sentinel = next;
next.addEventListener('release', () => {
sentinel = null;
dispatch(element, 'RELEASED');
});
dispatch(element, 'ACTIVE');
return { state: 'granted' };
}
catch (e) {
const name = e?.name;
const errorCode = name === 'NotAllowedError' ? 'NOT_ALLOWED' : 'UNKNOWN';
return {
state: 'error',
errorCode,
message: e?.message ? String(e.message) : String(e)
};
}
}
function installVisibilityListener(element) {
if (visibilityListenerInstalled) {
return;
}
visibilityListenerInstalled = true;
document.addEventListener('visibilitychange', () => {
if (wanted && !sentinel && document.visibilityState === 'visible') {
acquire(element);
}
});
}
const $wnd = window;
$wnd.Vaadin ??= {};
$wnd.Vaadin.Flow ??= {};
$wnd.Vaadin.Flow.wakeLock = {
request(element) {
wanted = true;
installVisibilityListener(element);
if (document.visibilityState !== 'visible') {
// The browser will not grant a lock while the page is hidden; the
// visibilitychange listener will pick it up on the next 'visible'.
return Promise.resolve({ state: 'deferred' });
}
return acquire(element);
},
async release(element) {
wanted = false;
if (!sentinel) {
return;
}
const current = sentinel;
sentinel = null;
try {
await current.release();
}
catch (_e) {
// Ignore; the 'release' event listener installed in acquire() also
// dispatches RELEASED, so the state still reaches the server even when
// the explicit release() call rejects.
}
dispatch(element, 'RELEASED');
},
queryAvailability() {
if (!window.isSecureContext) {
return 'UNSUPPORTED';
}
return 'wakeLock' in navigator ? 'SUPPORTED' : 'UNSUPPORTED';
}
};
export {};
//# sourceMappingURL=WakeLock.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
/**
* Returns whether the current browser exposes the Web Share API
* (`navigator.share`). Used by the bootstrap path to seed the server-side
* support signal without waiting for a DOM event.
*/
export declare function isShareSupported(): boolean;
@@ -0,0 +1,24 @@
/*
* 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 whether the current browser exposes the Web Share API
* (`navigator.share`). Used by the bootstrap path to seed the server-side
* support signal without waiting for a DOM event.
*/
export function isShareSupported() {
return typeof navigator.share === 'function';
}
//# sourceMappingURL=WebShare.js.map
@@ -0,0 +1 @@
{"version":3,"file":"WebShare.js","sourceRoot":"","sources":["../../../../src/main/frontend/WebShare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,SAAS,CAAC,KAAK,KAAK,UAAU,CAAC;AAC/C,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 * Returns whether the current browser exposes the Web Share API\n * (`navigator.share`). Used by the bootstrap path to seed the server-side\n * support signal without waiting for a DOM event.\n */\nexport function isShareSupported(): boolean {\n return typeof navigator.share === 'function';\n}\n"]}
@@ -0,0 +1,244 @@
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
import { timeOut } from '@vaadin/component-base/src/async.js';
import { ComboBoxPlaceholder } from '@vaadin/combo-box/src/vaadin-combo-box-placeholder.js';
window.Vaadin.Flow.comboBoxConnector = {};
window.Vaadin.Flow.comboBoxConnector.initLazy = (comboBox) => {
// Check whether the connector was already initialized for the ComboBox
if (comboBox.$connector) {
return;
}
comboBox.$connector = {};
let cache = {};
const placeHolder = new window.Vaadin.ComboBoxPlaceholder();
let lastTypedFilter = '';
let lastRequestedRange = [-1, -1];
let lastRequestedFilter = '';
let needsDataCommunicatorReset = false;
const dataProvider = function (params, callback) {
if (params.pageSize != comboBox.pageSize) {
throw 'Invalid pageSize';
}
if (comboBox._clientSideFilter) {
if (cache[0]) {
performClientSideFilter(cache[0], params.filter, callback);
return;
}
// First fetch: ignore the typed filter so we get the full dataset
params = { ...params, filter: '' };
}
if (lastTypedFilter !== params.filter) {
cache = {};
lastTypedFilter = params.filter;
lastRequestedRange = [-1, -1];
comboBox._filterDebouncer = Debouncer.debounce(
comboBox._filterDebouncer,
timeOut.after(comboBox._filterTimeout ?? 500),
() => {
// Filter cycled back to what server last received — force re-emit.
if (params.filter === lastRequestedFilter) {
needsDataCommunicatorReset = true;
}
comboBox.clearCache();
}
);
return;
}
if (comboBox._filterDebouncer?.isActive()) {
return;
}
// If buffer-prefetch already cached this page, commit it without a server
// round-trip; otherwise ask the server.
if (cache[params.page]) {
callback(cache[params.page], comboBox.size);
return;
}
comboBox.$connector.requestPage(params.page, params.filter);
};
comboBox.$connector.getViewportRange = function () {
const indices = Array.from(comboBox._scroller?.children ?? [])
.map((child) => child.index)
.filter((index) => Number.isFinite(index))
.sort((a, b) => a - b);
if (indices.length === 0) {
return [0, 0];
}
return [indices[0], indices[indices.length - 1]];
};
comboBox.$connector.requestPage = function (page, filter) {
let viewportRange = comboBox.$connector.getViewportRange();
const buffer = viewportRange[1] - viewportRange[0];
const sizeLimit = Number.isFinite(comboBox.size) ? comboBox.size : Number.POSITIVE_INFINITY;
viewportRange[0] = Math.max(viewportRange[0] - buffer, 0);
viewportRange[1] = Math.min(viewportRange[1] + buffer, sizeLimit - 1);
let viewportPageRange = [
Math.floor(viewportRange[0] / comboBox.pageSize),
Math.floor(viewportRange[1] / comboBox.pageSize)
];
// Collapse to the requested page when it's outside the current viewport,
// so confirm() can resolve callbacks left behind by fast scrolling.
if (page < viewportPageRange[0] || page > viewportPageRange[1]) {
viewportPageRange = [page, page];
}
if (lastRequestedRange[0] != viewportPageRange[0] || lastRequestedRange[1] != viewportPageRange[1]) {
const startIndex = viewportPageRange[0] * comboBox.pageSize;
const endIndex = (viewportPageRange[1] + 1) * comboBox.pageSize;
comboBox.$server.setViewportRange(startIndex, endIndex - startIndex, filter);
}
if (needsDataCommunicatorReset) {
comboBox.$server.resetDataCommunicator();
needsDataCommunicatorReset = false;
}
lastRequestedRange = viewportPageRange;
lastRequestedFilter = filter;
};
comboBox.$connector.clear = (start, length) => {
const { pageSize } = comboBox;
const firstPage = Math.floor(start / pageSize);
const lastPage = firstPage + Math.ceil(length / pageSize);
for (let page = firstPage; page < lastPage; page++) {
delete cache[page];
}
for (let index = firstPage * pageSize; index < lastPage * pageSize; index++) {
if (comboBox.filteredItems[index]) {
comboBox.filteredItems[index] = placeHolder;
}
}
};
comboBox.$connector.filter = (item, filter) => {
filter = filter ? filter.toString().toLowerCase() : '';
return comboBox._getItemLabel(item, comboBox.itemLabelPath).toString().toLowerCase().indexOf(filter) > -1;
};
comboBox.$connector.set = (index, items, filter) => {
if (filter !== lastTypedFilter) {
return;
}
if (index % comboBox.pageSize != 0) {
throw 'Got new data to index ' + index + ' which is not aligned with the page size of ' + comboBox.pageSize;
}
const { pendingRequests } = comboBox.__dataProviderController.rootCache;
if (index === 0 && items.length === 0 && pendingRequests[0]) {
// Makes sure that the dataProvider callback is called even when server
// returns empty data set (no items match the filter).
cache[0] = [];
return;
}
const firstPageToSet = index / comboBox.pageSize;
const updatedPageCount = Math.ceil(items.length / comboBox.pageSize);
for (let i = 0; i < updatedPageCount; i++) {
let page = firstPageToSet + i;
let slice = items.slice(i * comboBox.pageSize, (i + 1) * comboBox.pageSize);
cache[page] = slice;
}
};
comboBox.$connector.updateData = (items) => {
const itemsMap = new Map(items.map((item) => [item.key, item]));
comboBox.filteredItems = comboBox.filteredItems.map((item) => {
return itemsMap.get(item.key) || item;
});
};
comboBox.$connector.updateSize = function (newSize) {
if (!comboBox._clientSideFilter) {
// FIXME: It may be that this size set is unnecessary, since when
// providing data to combobox via callback we may use data's size.
// However, if this size reflect the whole data size, including
// data not fetched yet into client side, and combobox expect it
// to be set as such, the at least, we don't need it in case the
// filter is clientSide only, since it'll increase the height of
// the popup at only at first user filter to this size, while the
// filtered items count are less.
comboBox.size = newSize;
}
};
comboBox.$connector.reset = function () {
comboBox._filterDebouncer?.cancel();
comboBox._filterDebouncer = null;
cache = {};
lastRequestedRange = [-1, -1];
lastTypedFilter = '';
comboBox.clearCache();
};
comboBox.$connector.confirm = function (id, filter) {
if (filter !== lastTypedFilter) {
return;
}
// We're done applying changes from this batch, resolve pending
// callbacks
const { pendingRequests } = comboBox.__dataProviderController.rootCache;
Object.entries(pendingRequests).forEach(([page, callback]) => {
const items = cache[page];
if (comboBox._clientSideFilter && items) {
performClientSideFilter(items, comboBox.filter, callback);
return;
}
callback(items ?? [], comboBox.size);
delete cache[page];
});
// Let server know we're done
comboBox.$server.confirmUpdate(id);
};
// Perform filter on client side (here) using the items from specified page
// and submitting the filtered items to specified callback.
// The filter used is the one from combobox, not the lastFilter stored since
// that may not reflect user's input.
const performClientSideFilter = function (page, filter, callback) {
let filteredItems = page;
if (filter) {
filteredItems = page.filter((item) => comboBox.$connector.filter(item, filter));
}
callback(filteredItems, filteredItems.length);
};
// Prevent setting the custom value as the 'value'-prop automatically
comboBox.addEventListener('custom-value-set', (e) => e.preventDefault());
comboBox.itemClassNameGenerator = function (item) {
return item.className || '';
};
// Assign last, after all `$connector` functions are defined.
comboBox.dataProvider = dataProvider;
};
window.Vaadin.ComboBoxPlaceholder = ComboBoxPlaceholder;
@@ -0,0 +1,124 @@
function getContainer(appId, nodeId) {
try {
return window.Vaadin.Flow.clients[appId].getByNodeId(nodeId);
} catch (error) {
console.error('Could not get node %s from app %s', nodeId, appId);
console.error(error);
}
}
/**
* Initializes the connector for a context menu element.
*
* @param {HTMLElement} contextMenu
* @param {string} appId
*/
function initLazy(contextMenu, appId) {
if (contextMenu.$connector) {
return;
}
contextMenu.$connector = {
/**
* Generates and assigns the items to the context menu.
*
* @param {number} nodeId
*/
generateItems(nodeId) {
const items = generateItemsTree(appId, nodeId);
contextMenu.items = items;
}
};
}
/**
* Generates an items tree compatible with the context-menu web component
* by traversing the given Flow DOM tree of context menu item nodes
* whose root node is identified by the `nodeId` argument.
*
* The app id is required to access the store of Flow DOM nodes.
*
* @param {string} appId
* @param {number} nodeId
*/
function generateItemsTree(appId, nodeId) {
const container = getContainer(appId, nodeId);
if (!container) {
return;
}
return Array.from(container.children).map((child) => {
const item = {
component: child,
checked: child._checked,
keepOpen: child._keepOpen,
className: child.className,
theme: child.__theme,
tooltip: child.tooltip,
tooltipPosition: child.tooltipPosition
};
// Do not hardcode tag name to allow `vaadin-menu-bar-item`
if (child._hasVaadinItemMixin && child._containerNodeId) {
item.children = generateItemsTree(appId, child._containerNodeId);
}
child._item = item;
return item;
});
}
/**
* Sets the checked state for a context menu item.
*
* This method is supposed to be called when the context menu item is closed,
* so there is no need for triggering a re-render eagarly.
*
* @param {HTMLElement} component
* @param {boolean} checked
*/
function setChecked(component, checked) {
if (component._item) {
component._item.checked = checked;
// Set the attribute in the connector to show the checkmark
// without having to re-render the whole menu while opened.
if (component._item.keepOpen) {
component.toggleAttribute('menu-item-checked', checked);
}
}
}
/**
* Sets the keep open state for a context menu item.
*
* @param {HTMLElement} component
* @param {boolean} keepOpen
*/
function setKeepOpen(component, keepOpen) {
if (component._item) {
component._item.keepOpen = keepOpen;
}
}
/**
* Sets the theme for a context menu item.
*
* This method is supposed to be called when the context menu item is closed,
* so there is no need for triggering a re-render eagarly.
*
* @param {HTMLElement} component
* @param {string | undefined | null} theme
*/
function setTheme(component, theme) {
if (component._item) {
component._item.theme = theme;
}
}
window.Vaadin.Flow.contextMenuConnector = {
initLazy,
generateItemsTree,
setChecked,
setKeepOpen,
setTheme
};
@@ -0,0 +1,67 @@
import * as Gestures from '@vaadin/component-base/src/gestures.js';
function init(target) {
if (target.$contextMenuTargetConnector) {
return;
}
target.$contextMenuTargetConnector = {
openOnHandler(e) {
// used by Grid to prevent context menu on selection column click
if (target.preventContextMenu && target.preventContextMenu(e)) {
return;
}
e.preventDefault();
e.stopPropagation();
// The menu is opened later, after a server round-trip, when the event has
// finished dispatching and `composedPath()` returns an empty array. Capture
// the composed path now so the menu can resolve the target inside a shadow
// root (e.g. a grid cell) instead of the retargeted host.
e.__composedPath = e.composedPath();
this.$contextMenuTargetConnector.openEvent = e;
let detail = {};
if (target.getContextMenuBeforeOpenDetail) {
detail = target.getContextMenuBeforeOpenDetail(e);
}
target.dispatchEvent(
new CustomEvent('vaadin-context-menu-before-open', {
detail: detail
})
);
},
updateOpenOn(eventType) {
this.removeListener();
this.openOnEventType = eventType;
customElements.whenDefined('vaadin-context-menu').then(() => {
if (Gestures.gestures[eventType]) {
Gestures.addListener(target, eventType, this.openOnHandler);
} else {
target.addEventListener(eventType, this.openOnHandler);
}
});
},
removeListener() {
if (this.openOnEventType) {
if (Gestures.gestures[this.openOnEventType]) {
Gestures.removeListener(target, this.openOnEventType, this.openOnHandler);
} else {
target.removeEventListener(this.openOnEventType, this.openOnHandler);
}
}
},
openMenu(contextMenu) {
contextMenu.open(this.openEvent);
},
removeConnector() {
this.removeListener();
target.$contextMenuTargetConnector = undefined;
}
};
}
window.Vaadin.Flow.contextMenuTargetConnector = { init };
@@ -0,0 +1,179 @@
import dateFnsFormat from 'date-fns/format';
import dateFnsParse from 'date-fns/parse';
import dateFnsIsValid from 'date-fns/isValid';
import { extractDateParts, parseDate as _parseDate } from '@vaadin/date-picker/src/vaadin-date-picker-helper.js';
window.Vaadin.Flow.datepickerConnector = {};
window.Vaadin.Flow.datepickerConnector.initLazy = (datepicker) => {
// Check whether the connector was already initialized for the datepicker
if (datepicker.$connector) {
return;
}
datepicker.$connector = {};
const createLocaleBasedDateFormat = function (locale) {
try {
// Check whether the locale is supported or not
new Date().toLocaleDateString(locale);
} catch (e) {
console.warn('The locale is not supported, using default format setting (ISO 8601).');
return 'yyyy-MM-dd';
}
// format test date and convert to date-fns pattern
const testDate = new Date(Date.UTC(1234, 4, 6));
let pattern = testDate.toLocaleDateString(locale, { timeZone: 'UTC' });
pattern = pattern
// escape date-fns pattern letters by enclosing them in single quotes
.replace(/([a-zA-Z]+)/g, "'$1'")
// insert date placeholder
.replace('06', 'dd')
.replace('6', 'd')
// insert month placeholder
.replace('05', 'MM')
.replace('5', 'M')
// insert year placeholder
.replace('1234', 'yyyy');
const isValidPattern = pattern.includes('d') && pattern.includes('M') && pattern.includes('y');
if (!isValidPattern) {
console.warn('The locale is not supported, using default format setting (ISO 8601).');
return 'yyyy-MM-dd';
}
return pattern;
};
function createFormatterAndParser(formats) {
if (!formats || formats.length === 0) {
throw new Error('Array of custom date formats is null or empty');
}
function getShortYearFormat(format) {
if (format.includes('yyyy') && !format.includes('yyyyy')) {
return format.replace('yyyy', 'yy');
}
if (format.includes('YYYY') && !format.includes('YYYYY')) {
return format.replace('YYYY', 'YY');
}
return undefined;
}
function isFormatWithYear(format) {
return format.includes('y') || format.includes('Y');
}
function isShortYearFormat(format) {
// Format is long if it includes a four-digit year.
return !format.includes('yyyy') && !format.includes('YYYY');
}
function getExtendedFormats(formats) {
return formats.reduce((acc, format) => {
// We first try to match the date with the shorter version,
// as short years are supported with the long date format.
if (isFormatWithYear(format) && !isShortYearFormat(format)) {
acc.push(getShortYearFormat(format));
}
acc.push(format);
return acc;
}, []);
}
function correctFullYear(date) {
// The last parsed date check handles the case where a four-digit year is parsed, then formatted
// as a two-digit year, and then parsed again. In this case we want to keep the century of the
// originally parsed year, instead of using the century of the reference date.
// Do not apply any correction if the previous parse attempt was failed.
if (datepicker.$connector._lastParseStatus === 'error') {
return;
}
// Update century if the last parsed date is the same except the century.
if (datepicker.$connector._lastParseStatus === 'successful') {
if (
datepicker.$connector._lastParsedDate.day === date.getDate() &&
datepicker.$connector._lastParsedDate.month === date.getMonth() &&
datepicker.$connector._lastParsedDate.year % 100 === date.getFullYear() % 100
) {
date.setFullYear(datepicker.$connector._lastParsedDate.year);
}
return;
}
// Update century if this is the first parse after overlay open.
const currentValue = _parseDate(datepicker.value);
if (
dateFnsIsValid(currentValue) &&
currentValue.getDate() === date.getDate() &&
currentValue.getMonth() === date.getMonth() &&
currentValue.getFullYear() % 100 === date.getFullYear() % 100
) {
date.setFullYear(currentValue.getFullYear());
}
}
function formatDate(dateParts) {
const format = formats[0];
const date = _parseDate(`${dateParts.year}-${dateParts.month + 1}-${dateParts.day}`);
return dateFnsFormat(date, format);
}
function doParseDate(dateString, format, referenceDate) {
// When format does not contain a year, then current year should be used.
const refDate = isFormatWithYear(format) ? referenceDate : new Date();
const date = dateFnsParse(dateString, format, refDate);
if (dateFnsIsValid(date)) {
if (isFormatWithYear(format) && isShortYearFormat(format)) {
correctFullYear(date);
}
return {
day: date.getDate(),
month: date.getMonth(),
year: date.getFullYear()
};
}
}
function parseDate(dateString) {
const referenceDate = _getReferenceDate();
for (let format of getExtendedFormats(formats)) {
const parsedDate = doParseDate(dateString, format, referenceDate);
if (parsedDate) {
datepicker.$connector._lastParseStatus = 'successful';
datepicker.$connector._lastParsedDate = parsedDate;
return parsedDate;
}
}
datepicker.$connector._lastParseStatus = 'error';
return false;
}
return {
formatDate: formatDate,
parseDate: parseDate
};
}
function _getReferenceDate() {
const { referenceDate } = datepicker.i18n;
return referenceDate ? new Date(referenceDate.year, referenceDate.month, referenceDate.day) : new Date();
}
datepicker.$connector.updateI18n = (locale, i18n) => {
// Either use custom formats specified in I18N, or create format from locale
const hasCustomFormats = i18n && i18n.dateFormats && i18n.dateFormats.length > 0;
if (i18n && i18n.referenceDate) {
i18n.referenceDate = extractDateParts(new Date(i18n.referenceDate));
}
const usedFormats = hasCustomFormats ? i18n.dateFormats : [createLocaleBasedDateFormat(locale)];
const formatterAndParser = createFormatterAndParser(usedFormats);
// Merge new I18N settings with formatting and parsing functions
datepicker.i18n = Object.assign({}, i18n, formatterAndParser);
};
datepicker.addEventListener('opened-changed', () => (datepicker.$connector._lastParseStatus = undefined));
};
@@ -0,0 +1,6 @@
document.addEventListener('click', (event) => {
const target = event.composedPath().find((node) => node.hasAttribute && node.hasAttribute('disableonclick'));
if (target) {
target.disabled = true;
}
});
@@ -0,0 +1,131 @@
window.Vaadin = window.Vaadin || {};
window.Vaadin.Flow = window.Vaadin.Flow || {};
window.Vaadin.Flow.dndConnector = {
__ondragenterListener: function (event) {
// TODO filter by data type
// TODO prevent dropping on itself (by default)
const effect = event.currentTarget['__dropEffect'];
if (!event.currentTarget.hasAttribute('disabled')) {
if (effect) {
event.dataTransfer.dropEffect = effect;
}
if (effect !== 'none') {
/* #7108: if drag moves on top of drop target's children, first another ondragenter event
* is fired and then a ondragleave event. This happens again once the drag
* moves on top of another children, or back on top of the drop target element.
* Thus need to "cancel" the following ondragleave, to not remove class name.
* Drop event will happen even when dropped to a child element. */
if (event.currentTarget.classList.contains('v-drag-over-target')) {
event.currentTarget['__skip-leave'] = true;
} else {
event.currentTarget.classList.add('v-drag-over-target');
}
// enables browser specific pseudo classes (at least FF)
event.preventDefault();
event.stopPropagation(); // don't let parents know
}
}
},
__ondragoverListener: function (event) {
// TODO filter by data type
// TODO filter by effectAllowed != dropEffect due to Safari & IE11 ?
if (!event.currentTarget.hasAttribute('disabled')) {
const effect = event.currentTarget['__dropEffect'];
if (effect) {
event.dataTransfer.dropEffect = effect;
}
// allows the drop && don't let parents know
event.preventDefault();
event.stopPropagation();
}
},
__ondragleaveListener: function (event) {
if (event.currentTarget['__skip-leave']) {
event.currentTarget['__skip-leave'] = false;
} else {
event.currentTarget.classList.remove('v-drag-over-target');
}
// #7109 need to stop or any parent drop target might not get highlighted,
// as ondragenter for it is fired before the child gets dragleave.
event.stopPropagation();
},
__ondropListener: function (event) {
const effect = event.currentTarget['__dropEffect'];
if (effect) {
event.dataTransfer.dropEffect = effect;
}
event.currentTarget.classList.remove('v-drag-over-target');
// prevent browser handling && don't let parents know
event.preventDefault();
event.stopPropagation();
},
updateDropTarget: function (element) {
if (element['__active']) {
element.addEventListener('dragenter', this.__ondragenterListener, false);
element.addEventListener('dragover', this.__ondragoverListener, false);
element.addEventListener('dragleave', this.__ondragleaveListener, false);
element.addEventListener('drop', this.__ondropListener, false);
} else {
element.removeEventListener('dragenter', this.__ondragenterListener, false);
element.removeEventListener('dragover', this.__ondragoverListener, false);
element.removeEventListener('dragleave', this.__ondragleaveListener, false);
element.removeEventListener('drop', this.__ondropListener, false);
element.classList.remove('v-drag-over-target');
}
},
/** DRAG SOURCE METHODS: */
__dragstartListener: function (event) {
event.stopPropagation();
event.dataTransfer.setData('text/plain', '');
if (event.currentTarget.hasAttribute('disabled')) {
event.preventDefault();
} else {
if (event.currentTarget['__effectAllowed']) {
event.dataTransfer.effectAllowed = event.currentTarget['__effectAllowed'];
}
event.currentTarget.classList.add('v-dragged');
}
if (event.currentTarget.__dragImage) {
if (event.currentTarget.__dragImage.style.display === 'none') {
event.currentTarget.__dragImage.style.display = 'block';
event.currentTarget.classList.add('shown');
}
event.dataTransfer.setDragImage(
event.currentTarget.__dragImage,
event.currentTarget.__dragImageOffsetX,
event.currentTarget.__dragImageOffsetY
);
}
},
__dragendListener: function (event) {
event.currentTarget.classList.remove('v-dragged');
if (event.currentTarget.classList.contains('shown')) {
event.currentTarget.classList.remove('shown');
event.currentTarget.__dragImage.style.display = 'none';
}
},
updateDragSource: function (element) {
if (element['draggable']) {
element.addEventListener('dragstart', this.__dragstartListener, false);
element.addEventListener('dragend', this.__dragendListener, false);
} else {
element.removeEventListener('dragstart', this.__dragstartListener, false);
element.removeEventListener('dragend', this.__dragendListener, false);
}
},
setDragImage: function (dragImage, offsetX, offsetY, dragSource) {
dragSource.__dragImage = dragImage;
dragSource.__dragImageOffsetX = offsetX;
dragSource.__dragImageOffsetY = offsetY;
}
};
@@ -0,0 +1,68 @@
import { noChange } from 'lit';
import { directive, PartType } from 'lit/directive.js';
import { AsyncDirective } from 'lit/async-directive.js';
class FlowComponentDirective extends AsyncDirective {
constructor(partInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error(`${this.constructor.directiveName}() can only be used in child bindings`);
}
}
update(part, [appid, nodeid]) {
this.updateContent(part, appid, nodeid);
return noChange;
}
updateContent(part, appid, nodeid) {
const { parentNode, startNode } = part;
this.__parentNode = parentNode;
const hasNewNodeId = nodeid !== undefined && nodeid !== null;
const newNode = hasNewNodeId ? this.getNewNode(appid, nodeid) : null;
const oldNode = this.getOldNode(part);
clearTimeout(this.__parentNode.__nodeRetryTimeout);
if (hasNewNodeId && !newNode) {
// If the node is not found, try again later.
this.__parentNode.__nodeRetryTimeout = setTimeout(() => this.updateContent(part, appid, nodeid));
} else if (oldNode === newNode) {
return;
} else if (oldNode && newNode) {
parentNode.replaceChild(newNode, oldNode);
} else if (oldNode) {
parentNode.removeChild(oldNode);
} else if (newNode) {
startNode.after(newNode);
}
}
getNewNode(appid, nodeid) {
return window.Vaadin.Flow.clients[appid].getByNodeId(nodeid);
}
getOldNode(part) {
const { startNode, endNode } = part;
if (startNode.nextSibling === endNode) {
return;
}
return startNode.nextSibling;
}
disconnected() {
clearTimeout(this.__parentNode.__nodeRetryTimeout);
}
}
/**
* Renders the given flow component node.
*
* WARNING: This directive is not intended for public use.
*
* @param {string} appid
* @param {number} nodeid
* @private
*/
export const flowComponentDirective = directive(FlowComponentDirective);
@@ -0,0 +1,47 @@
import { flowComponentDirective } from './flow-component-directive.js';
import { render, html as litHtml } from 'lit';
/**
* Returns the requested node in a form suitable for Lit template interpolation.
* @param {string} appid
* @param {number} nodeid
* @returns {any} a Lit directive
*/
function getNode(appid, nodeid) {
return flowComponentDirective(appid, nodeid);
}
/**
* Sets the nodes defined by the given node ids as the child nodes of the
* given root element.
* @param {string} appid
* @param {number[]} nodeIds
* @param {Element} root
*/
function setChildNodes(appid, nodeIds, root) {
render(litHtml`${nodeIds.map((id) => flowComponentDirective(appid, id))}`, root);
}
/**
* SimpleElementBindingStrategy::addChildren uses insertBefore to add child
* elements to the container. When the children are manually placed under
* another element, the call to insertBefore can occasionally fail due to
* an invalid reference node.
*
* This is a temporary workaround which patches the container's native API
* to not fail when called with invalid arguments.
*/
function patchVirtualContainer(container) {
const originalInsertBefore = container.insertBefore;
container.insertBefore = function (newNode, referenceNode) {
if (referenceNode && referenceNode.parentNode === this) {
return originalInsertBefore.call(this, newNode, referenceNode);
} else {
return originalInsertBefore.call(this, newNode, null);
}
};
}
window.Vaadin ||= {};
window.Vaadin.FlowComponentHost ||= { patchVirtualContainer, getNode, setChildNodes };
@@ -0,0 +1,725 @@
// @ts-nocheck
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
import { timeOut, animationFrame } from '@vaadin/component-base/src/async.js';
import { Grid } from '@vaadin/grid/src/vaadin-grid.js';
import { isFocusable } from '@vaadin/grid/src/vaadin-grid-active-item-mixin.js';
import { GridFlowSelectionColumn } from './vaadin-grid-flow-selection-column.js';
window.Vaadin.Flow.gridConnector = {};
window.Vaadin.Flow.gridConnector.initLazy = (grid) => {
// Check whether the connector was already initialized for the grid
if (grid.$connector) {
return;
}
const dataProviderController = grid._dataProviderController;
const requestDebouncerDelay = 150;
let requestDebouncer;
let lastRequestedRange = [0, 0];
const validSelectionModes = ['SINGLE', 'NONE', 'MULTI'];
let selectedKeys = {};
let selectionMode = 'SINGLE';
let sorterDirectionsSetFromServer = false;
grid.size = 0; // To avoid NaN here and there before we get proper data
grid.itemIdPath = 'key';
grid.$connector = {};
grid.$connector.hasRootRequestQueue = () => {
const { pendingRequests } = dataProviderController.rootCache;
return Object.keys(pendingRequests).length > 0 || !!requestDebouncer?.isActive();
};
grid.$connector.doSelection = function (items, userOriginated) {
if (selectionMode === 'NONE' || !items.length || (userOriginated && grid.hasAttribute('disabled'))) {
return;
}
if (selectionMode === 'SINGLE') {
selectedKeys = {};
}
let selectedItemsChanged = false;
items.forEach((item) => {
const selectable = !userOriginated || grid.isItemSelectable(item);
selectedItemsChanged = selectedItemsChanged || selectable;
if (item && selectable) {
selectedKeys[item.key] = item;
item.selected = true;
if (userOriginated) {
grid.$server.select(item.key);
}
}
// FYI: In single selection mode, the server can send items = [null]
// which means a "Deselect All" command.
const isSelectedItemDifferentOrNull = !grid.activeItem || !item || item.key != grid.activeItem.key;
if (!userOriginated && selectionMode === 'SINGLE' && isSelectedItemDifferentOrNull) {
grid.activeItem = item;
}
});
if (selectedItemsChanged) {
grid.selectedItems = Object.values(selectedKeys);
}
};
grid.$connector.doDeselection = function (items, userOriginated) {
if (selectionMode === 'NONE' || !items.length || (userOriginated && grid.hasAttribute('disabled'))) {
return;
}
const updatedSelectedItems = grid.selectedItems.slice();
while (items.length) {
const itemToDeselect = items.shift();
const selectable = !userOriginated || grid.isItemSelectable(itemToDeselect);
if (!selectable) {
continue;
}
for (let i = 0; i < updatedSelectedItems.length; i++) {
const selectedItem = updatedSelectedItems[i];
if (itemToDeselect?.key === selectedItem.key) {
updatedSelectedItems.splice(i, 1);
break;
}
}
if (itemToDeselect) {
delete selectedKeys[itemToDeselect.key];
delete itemToDeselect.selected;
if (userOriginated) {
grid.$server.deselect(itemToDeselect.key);
}
}
}
grid.selectedItems = updatedSelectedItems;
};
grid.__activeItemChanged = function (newVal, oldVal) {
if (selectionMode != 'SINGLE') {
return;
}
if (!newVal) {
if (oldVal && selectedKeys[oldVal.key]) {
if (grid.__deselectDisallowed) {
grid.activeItem = oldVal;
} else {
// The item instance may have changed since the item was stored as active item
// and information such as whether the item may be selected or deselected may
// be stale. Use data provider controller to get updated instance from grid
// cache.
oldVal = dataProviderController.getItemContext(oldVal).item;
grid.$connector.doDeselection([oldVal], true);
}
}
} else if (!selectedKeys[newVal.key]) {
grid.$connector.doSelection([newVal], true);
}
};
grid._createPropertyObserver('activeItem', '__activeItemChanged', true);
grid.__activeItemChangedDetails = function (newVal, oldVal) {
if (grid.__disallowDetailsOnClick) {
return;
}
// when grid is attached, newVal is not set and oldVal is undefined
// do nothing
if (newVal == null && oldVal === undefined) {
return;
}
if (newVal && !newVal.detailsOpened) {
grid.$server.setDetailsVisible(newVal.key);
} else {
grid.$server.setDetailsVisible(null);
}
};
grid._createPropertyObserver('activeItem', '__activeItemChangedDetails', true);
grid.$connector.getViewportRange = function () {
const renderedRows = grid._getRenderedRows();
return [renderedRows.at(0)?.index ?? 0, renderedRows.at(-1)?.index ?? 0];
};
grid.$connector.requestPage = function (page) {
// Adjust the requested page to be within the valid range in case
// the grid size has changed while fetchPage was debounced.
page = Math.min(page, Math.floor((grid.size - 1) / grid.pageSize));
// Determine what to fetch based on scroll position and not only
// what grid asked for
let viewportRange = grid.$connector.getViewportRange();
// The buffer size could be multiplied by some constant defined by the user,
// if he needs to reduce the number of items sent to the Grid to improve performance
// or to increase it to make Grid smoother when scrolling
const buffer = viewportRange[1] - viewportRange[0];
viewportRange[0] = Math.max(viewportRange[0] - buffer, 0);
viewportRange[1] = Math.min(viewportRange[1] + buffer, grid.size);
let viewportPageRange = [
Math.floor(viewportRange[0] / grid.pageSize),
Math.floor(viewportRange[1] / grid.pageSize)
];
// When the viewport doesn't contain the requested page or it doesn't contain any items from
// the requested level at all, it means that the scroll position has changed while fetchPage
// was debounced. For example, it can happen if the user scrolls the grid to the bottom and
// then immediately back to the top. In this case, the request for the last page will be left
// hanging. To avoid this, as a workaround, we reset the range to only include the requested page
// to make sure all hanging requests are resolved. After that, the grid requests the first page
// or whatever in the viewport again.
if (page < viewportPageRange[0] || page > viewportPageRange[1]) {
viewportPageRange = [page, page];
}
if (lastRequestedRange[0] != viewportPageRange[0] || lastRequestedRange[1] != viewportPageRange[1]) {
lastRequestedRange = viewportPageRange;
const pageCount = viewportPageRange[1] - viewportPageRange[0] + 1;
grid.$server.setViewportRange(viewportPageRange[0] * grid.pageSize, pageCount * grid.pageSize);
}
};
grid.dataProvider = function (params, callback) {
if (params.pageSize != grid.pageSize) {
throw 'Invalid pageSize';
}
// size is controlled by the server (data communicator), so if the
// size is zero, we know that there is no data to fetch.
// This also prevents an empty grid getting stuck in a loading state.
// The connector does not cache empty pages, so if the grid requests
// data again, there would be no cache entry, causing a request to
// the server. However, the data communicator will never respond,
// as it assumes that the data is already cached.
if (grid.size === 0) {
callback([], 0);
return;
}
requestDebouncer = Debouncer.debounce(
requestDebouncer,
timeOut.after(grid._hasData ? requestDebouncerDelay : 0),
() => {
grid.$connector.requestPage(params.page);
}
);
};
grid.$connector.setSorterDirections = function (directions) {
sorterDirectionsSetFromServer = true;
setTimeout(() => {
try {
const sorters = Array.from(grid.querySelectorAll('vaadin-grid-sorter'));
// Sorters for hidden columns are removed from DOM but stored in the web component.
// We need to ensure that all the sorters are reset when using `grid.sort(null)`.
grid._sorters.forEach((sorter) => {
if (!sorters.includes(sorter)) {
sorters.push(sorter);
}
});
sorters.forEach((sorter) => {
sorter.direction = null;
});
// Apply directions in correct order, depending on configured multi-sort priority.
// For the default "prepend" mode, directions need to be applied in reverse, in
// order for the sort indicators to match the order on the server. For "append"
// just keep the order passed from the server.
if (grid.multiSortPriority !== 'append') {
directions = directions.reverse();
}
directions.forEach(({ column, direction }) => {
sorters.forEach((sorter) => {
if (sorter.getAttribute('path') === column) {
sorter.direction = direction;
}
});
});
// Manually trigger a re-render of the sorter priority indicators
// in case some of the sorters were hidden while being updated above
// and therefore didn't notify the grid about their direction change.
grid.__applySorters();
} finally {
sorterDirectionsSetFromServer = false;
}
});
};
let preventUpdateVisibleRowsActive = 0;
function preventUpdateVisibleRows(callback) {
try {
preventUpdateVisibleRowsActive++;
callback();
} finally {
preventUpdateVisibleRowsActive--;
}
}
grid.__updateVisibleRows = function (...args) {
if (preventUpdateVisibleRowsActive === 0) {
Object.getPrototypeOf(this).__updateVisibleRows.call(this, ...args);
}
};
grid.__updateRow = function (row, ...args) {
Object.getPrototypeOf(this).__updateRow.call(this, row, ...args);
// since no row can be selected when selection mode is NONE
// if selectionMode is set to NONE, remove aria-selected attribute from the row
if (selectionMode === validSelectionModes[1]) {
// selectionMode === NONE
row.removeAttribute('aria-selected');
Array.from(row.children).forEach((cell) => cell.removeAttribute('aria-selected'));
}
};
grid.$connector.set = function (startIndex, items) {
const { rootCache } = dataProviderController;
items.forEach((item, i) => {
rootCache.items[startIndex + i] = item;
});
preventUpdateVisibleRows(() => {
grid.$connector.doSelection(items.filter((item) => item.selected));
grid.$connector.doDeselection(items.filter((item) => !item.selected && selectedKeys[item.key]));
items.forEach((item) => {
if (item.detailsOpened) {
grid.openItemDetails(item);
} else {
grid.closeItemDetails(item);
}
});
});
grid.__updateVisibleRows(startIndex, startIndex + items.length - 1);
};
/**
* Updates the given items for a non-hierarchical grid.
*
* @param updatedItems the updated items array
*/
grid.$connector.updateFlatData = function (updatedItems) {
const { rootCache } = dataProviderController;
updatedItems.forEach((item) => {
const itemContext = dataProviderController.getItemContext(item);
if (!itemContext) {
return;
}
const { index } = itemContext;
rootCache.items[index] = item;
preventUpdateVisibleRows(() => {
if (item.detailsOpened) {
grid.openItemDetails(item);
} else {
grid.closeItemDetails(item);
}
});
grid.__updateVisibleRows(index, index);
});
};
grid.$connector.clear = function (index, length) {
const { rootCache } = dataProviderController;
if (index % grid.pageSize != 0) {
throw 'Got cleared data for index ' + index + ' which is not aligned with the page size of ' + grid.pageSize;
}
const items = rootCache.items.slice(index, index + length).filter(Boolean);
if (items.length === 0) {
return;
}
preventUpdateVisibleRows(() => {
grid.$connector.doDeselection(items.filter((item) => selectedKeys[item.key]));
items.forEach((item) => grid.closeItemDetails(item));
});
for (let i = index; i < index + length; i++) {
rootCache.items[i] = undefined;
}
grid.__updateVisibleRows(index, index + length - 1);
};
grid.$connector.reset = function () {
dataProviderController.clearCache();
lastRequestedRange = [-1, -1];
requestDebouncer?.cancel();
grid.__updateVisibleRows();
};
grid.$connector.updateSize = (newSize) => (grid.size = newSize);
grid.$connector.updateUniqueItemIdPath = (path) => (grid.itemIdPath = path);
grid.$connector.confirm = function (id) {
// We're done applying changes from this batch, resolve pending
// callbacks
const { rootCache } = dataProviderController;
grid._hasData = true;
Object.entries(rootCache.pendingRequests).forEach(([page, callback]) => {
const lastAvailablePage = grid.size ? Math.ceil(grid.size / grid.pageSize) - 1 : 0;
// It's possible that the lastRequestedRange includes a page that's beyond lastAvailablePage if the grid's size got reduced during an ongoing data request
const lastRequestedRangeEnd = Math.min(lastRequestedRange[1], lastAvailablePage);
// Resolve if we have data or if we don't expect to get data
const startIndex = page * grid.pageSize;
if (rootCache.items[startIndex] !== undefined) {
// Cached data is available, resolve the callback
callback([]);
} else if (page < lastRequestedRange[0] || +page > lastRequestedRangeEnd) {
// No cached data, resolve the callback with an empty array
callback(new Array(grid.pageSize));
// Request grid for content update
grid.requestContentUpdate();
} else if (callback && grid.size === 0) {
// The grid has 0 items => resolve the callback with an empty array
callback([]);
}
});
// If all pending requests have already been resolved (which can happen
// for example if the server sent preloaded data while the grid had
// already made its own requests), cancel the request debouncer to
// prevent further unnecessary calls.
if (Object.keys(rootCache.pendingRequests).length === 0) {
requestDebouncer?.cancel();
lastRequestedRange = [-1, -1];
}
// Let server know we're done
grid.$server.confirmUpdate(id);
};
grid.$connector.setSelectionMode = function (mode) {
if ((typeof mode === 'string' || mode instanceof String) && validSelectionModes.indexOf(mode) >= 0) {
selectionMode = mode;
selectedKeys = {};
grid.selectedItems = [];
grid.$connector.updateMultiSelectable();
} else {
throw 'Attempted to set an invalid selection mode';
}
};
/*
* Manage aria-multiselectable attribute depending on the selection mode.
* see more: https://github.com/vaadin/web-components/issues/1536
* or: https://www.w3.org/TR/wai-aria-1.1/#aria-multiselectable
* For selection mode SINGLE, set the aria-multiselectable attribute to false
*/
grid.$connector.updateMultiSelectable = function () {
if (!grid.$) {
return;
}
if (selectionMode === validSelectionModes[0]) {
grid.$.table.setAttribute('aria-multiselectable', false);
// For selection mode NONE, remove the aria-multiselectable attribute
} else if (selectionMode === validSelectionModes[1]) {
grid.$.table.removeAttribute('aria-multiselectable');
// For selection mode MULTI, set aria-multiselectable to true
} else {
grid.$.table.setAttribute('aria-multiselectable', true);
}
};
// Have the multi-selectable state updated on attach
grid._createPropertyObserver('isAttached', () => grid.$connector.updateMultiSelectable());
const singleTimeRenderer = (renderer) => {
return (root) => {
if (renderer) {
renderer(root);
renderer = null;
}
};
};
grid.$connector.setHeaderRenderer = function (column, options) {
const { content, showSorter, sorterPath } = options;
if (content === null) {
column.headerRenderer = null;
return;
}
column.headerRenderer = singleTimeRenderer((root) => {
// Clear previous contents
root.innerHTML = '';
// Render sorter
let contentRoot = root;
if (showSorter) {
const sorter = document.createElement('vaadin-grid-sorter');
sorter.setAttribute('path', sorterPath);
const ariaLabel = content instanceof Node ? content.textContent : content;
if (ariaLabel) {
sorter.setAttribute('aria-label', `Sort by ${ariaLabel}`);
}
root.appendChild(sorter);
// Use sorter as content root
contentRoot = sorter;
}
// Add content
if (content instanceof Node) {
contentRoot.appendChild(content);
} else {
contentRoot.textContent = content;
}
});
};
// This method is overridden to prevent the grid web component from
// automatically excluding columns from sorting when they get hidden.
// In Flow, it's the developer's responsibility to remove the column
// from the backend sort order when the column gets hidden.
grid._getActiveSorters = function () {
return this._sorters.filter((sorter) => sorter.direction);
};
grid.__applySorters = function (...args) {
const sorters = grid._mapSorters();
const sortersChanged = JSON.stringify(grid._previousSorters) !== JSON.stringify(sorters);
// Update the _previousSorters in vaadin-grid-sort-mixin so that the __applySorters
// method in the mixin will skip calling clearCache().
//
// In Flow Grid's case, we never want to clear the cache eagerly when the sorter elements
// change due to one of the following reasons:
//
// 1. Sorted by user: The items in the new sort order need to be fetched from the server,
// and we want to avoid a heavy re-render before the updated items have actually been fetched.
//
// 2. Sorted programmatically on the server: The items in the new sort order have already
// been fetched and applied to the grid. The sorter element states are updated programmatically
// to reflect the new sort order, but there's no need to re-render the grid rows.
grid._previousSorters = sorters;
// Call the original __applySorters method in vaadin-grid-sort-mixin
Object.getPrototypeOf(this).__applySorters.call(this, ...args);
if (sortersChanged && !sorterDirectionsSetFromServer) {
grid.$server.sortersChanged(sorters);
}
};
grid.$connector.setFooterRenderer = function (column, options) {
const { content } = options;
if (content === null) {
column.footerRenderer = null;
return;
}
column.footerRenderer = singleTimeRenderer((root) => {
// Clear previous contents
root.innerHTML = '';
// Add content
if (content instanceof Node) {
root.appendChild(content);
} else {
root.textContent = content;
}
});
};
grid.addEventListener('vaadin-context-menu-before-open', function (e) {
const { key, columnId } = e.detail;
grid.$server.updateContextMenuTargetItem(key, columnId);
});
grid.getContextMenuBeforeOpenDetail = function (event) {
// For `contextmenu` events, we need to access the source event,
// when using open on click we just use the click event itself
const sourceEvent = event.detail.sourceEvent || event;
const eventContext = grid.getEventContext(sourceEvent);
const key = eventContext.item?.key || '';
const columnId = eventContext.column?.id || '';
return { key, columnId };
};
grid.preventContextMenu = function (event) {
const isLeftClick = event.type === 'click';
const { column } = grid.getEventContext(event);
return isLeftClick && column instanceof GridFlowSelectionColumn;
};
grid.addEventListener('click', (e) => _fireClickEvent(e, 'item-click'));
grid.addEventListener('dblclick', (e) => _fireClickEvent(e, 'item-double-click'));
grid.addEventListener('column-resize', (e) => {
const cols = grid._getColumnsInOrder().filter((col) => !col.hidden);
cols.forEach((col) => {
col.dispatchEvent(new CustomEvent('column-drag-resize'));
});
grid.dispatchEvent(
new CustomEvent('column-drag-resize', {
detail: {
resizedColumnKey: e.detail.resizedColumn._flowId
}
})
);
});
grid.addEventListener('column-reorder', (e) => {
const columns = grid._columnTree
.slice(0)
.pop()
.filter((c) => c._flowId)
.sort((b, a) => b._order - a._order)
.map((c) => c._flowId);
grid.dispatchEvent(
new CustomEvent('column-reorder-all-columns', {
detail: { columns }
})
);
});
grid.addEventListener('cell-focus', (e) => {
const eventContext = grid.getEventContext(e);
const expectedSectionValues = ['header', 'body', 'footer'];
if (expectedSectionValues.indexOf(eventContext.section) === -1) {
return;
}
grid.dispatchEvent(
new CustomEvent('grid-cell-focus', {
detail: {
itemKey: eventContext.item ? eventContext.item.key : null,
internalColumnId: eventContext.column ? eventContext.column._flowId : null,
section: eventContext.section
}
})
);
});
function _fireClickEvent(event, eventName) {
// Click event was handled by the component inside grid, do nothing.
if (event.defaultPrevented) {
return;
}
const path = event.composedPath();
const idx = path.findIndex((node) => node.localName === 'td' || node.localName === 'th');
const cell = path[idx];
const content = path.slice(0, idx);
// Do not fire item click event if the click originated inside a Vaadin overlay
// (Select, ComboBox, DatePicker, MenuBar, ContextMenu, ...). The overlay's
// menu/list lives in the host component's light DOM, so its clicks bubble
// through the cell — but by the time we get here, the overlay has typically
// been hidden synchronously, defeating the offsetParent-based isFocusable check.
if (content.some((node) => typeof node.localName === 'string' && node.localName.endsWith('-overlay'))) {
return;
}
// Do not fire item click event if cell content contains focusable elements.
// Use this instead of event.target to detect cases like icon inside button.
// See https://github.com/vaadin/flow-components/issues/4065
if (
content.some((node) => {
// Ignore focus buttons that the component renders into cells in focus button mode on MacOS
const focusable = cell?._focusButton !== node && isFocusable(node);
return focusable || node instanceof HTMLLabelElement;
})
) {
return;
}
const eventContext = grid.getEventContext(event);
const section = eventContext.section;
if (eventContext.item && section !== 'details') {
event.itemKey = eventContext.item.key;
// if you have a details-renderer, getEventContext().column is undefined
if (eventContext.column) {
event.internalColumnId = eventContext.column._flowId;
}
grid.dispatchEvent(new CustomEvent(eventName, { detail: event }));
}
}
grid.cellPartNameGenerator = function (column, rowData) {
const part = rowData.item.part;
if (!part) {
return;
}
return (part.row || '') + ' ' + ((column && part[column._flowId]) || '');
};
grid.dropFilter = (rowData) => rowData.item && !rowData.item.dropDisabled;
grid.dragFilter = (rowData) => rowData.item && !rowData.item.dragDisabled;
grid.addEventListener('grid-dragstart', (e) => {
if (grid._isSelected(e.detail.draggedItems[0])) {
// Dragging selected (possibly multiple) items
if (grid.__selectionDragData) {
Object.keys(grid.__selectionDragData).forEach((type) => {
e.detail.setDragData(type, grid.__selectionDragData[type]);
});
} else {
(grid.__dragDataTypes || []).forEach((type) => {
e.detail.setDragData(type, e.detail.draggedItems.map((item) => item.dragData[type]).join('\n'));
});
}
if (grid.__selectionDraggedItemsCount > 1) {
e.detail.setDraggedItemsCount(grid.__selectionDraggedItemsCount);
}
} else {
// Dragging just one (non-selected) item
(grid.__dragDataTypes || []).forEach((type) => {
e.detail.setDragData(type, e.detail.draggedItems[0].dragData[type]);
});
}
});
grid.isItemSelectable = (item) => {
// If there is no selectable data, assume the item is selectable
return item?.selectable === undefined || item.selectable;
};
function isRowFullyInViewport(row) {
const rowRect = row.getBoundingClientRect();
const tableRect = grid.$.table.getBoundingClientRect();
const headerRect = grid.$.header.getBoundingClientRect();
const footerRect = grid.$.footer.getBoundingClientRect();
return rowRect.top >= tableRect.top + headerRect.height && rowRect.bottom <= tableRect.bottom - footerRect.height;
}
grid.$connector.scrollToItem = function (itemKey, ...args) {
const targetRow = grid._getRenderedRows().find((row) => {
const { item } = grid.__getRowModel(row);
return grid.getItemId(item) === itemKey;
});
if (targetRow && isRowFullyInViewport(targetRow)) {
return;
}
grid.scrollToIndex(...args);
};
};

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