tina4-python 3.13.47__tar.gz → 3.13.49__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tina4_python-3.13.47 → tina4_python-3.13.49}/PKG-INFO +1 -1
- {tina4_python-3.13.47 → tina4_python-3.13.49}/pyproject.toml +1 -1
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/i18n/__init__.py +59 -20
- tina4_python-3.13.49/tina4_python/public/js/tina4js.min.js +48 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/swagger/__init__.py +14 -82
- tina4_python-3.13.47/tina4_python/public/js/tina4js.min.js +0 -48
- {tina4_python-3.13.47 → tina4_python-3.13.49}/.gitignore +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/README.md +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/env.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -9,10 +9,27 @@ Simple key-based translations loaded from JSON files.
|
|
|
9
9
|
_("greeting") # "Hello" or "Bonjour" depending on locale
|
|
10
10
|
"""
|
|
11
11
|
import os
|
|
12
|
+
import re
|
|
12
13
|
import json
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
_PLACEHOLDER = re.compile(r"\{(\w+)\}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _interpolate(template: str, params: dict) -> str:
|
|
21
|
+
"""Substitute {name} placeholders from params.
|
|
22
|
+
|
|
23
|
+
Partial + literal-leftover: a placeholder present in params is replaced; a
|
|
24
|
+
missing or malformed placeholder ({x.y}, {n:d}, a lone brace) is left
|
|
25
|
+
untouched. Never raises -- a bad template must not crash t().
|
|
26
|
+
"""
|
|
27
|
+
return _PLACEHOLDER.sub(
|
|
28
|
+
lambda m: str(params[m.group(1)]) if m.group(1) in params else m.group(0),
|
|
29
|
+
template,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
16
33
|
class I18n:
|
|
17
34
|
"""Internationalization support with JSON translation files.
|
|
18
35
|
|
|
@@ -61,12 +78,11 @@ class I18n:
|
|
|
61
78
|
if value is None:
|
|
62
79
|
value = key
|
|
63
80
|
|
|
64
|
-
# Interpolate
|
|
81
|
+
# Interpolate {placeholder} tokens. Partial substitution: each token
|
|
82
|
+
# present in kwargs is replaced; a missing or malformed placeholder is
|
|
83
|
+
# left literal. Never raises (a bad template must not crash t()).
|
|
65
84
|
if kwargs:
|
|
66
|
-
|
|
67
|
-
value = value.format(**kwargs)
|
|
68
|
-
except (KeyError, IndexError):
|
|
69
|
-
pass
|
|
85
|
+
value = _interpolate(value, kwargs)
|
|
70
86
|
|
|
71
87
|
return value
|
|
72
88
|
|
|
@@ -86,9 +102,10 @@ class I18n:
|
|
|
86
102
|
if locale:
|
|
87
103
|
old = self._current_locale
|
|
88
104
|
self.locale = locale
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
try:
|
|
106
|
+
return self.t(key, **(params or {}))
|
|
107
|
+
finally:
|
|
108
|
+
self.locale = old
|
|
92
109
|
return self.t(key, **(params or {}))
|
|
93
110
|
|
|
94
111
|
def load_translations(self, locale: str) -> dict:
|
|
@@ -183,28 +200,50 @@ class I18n:
|
|
|
183
200
|
|
|
184
201
|
@staticmethod
|
|
185
202
|
def _flatten(data: dict, prefix: str = "") -> dict:
|
|
186
|
-
"""Flatten nested dict
|
|
203
|
+
"""Flatten a nested dict to dot-paths, then add leaf-key aliases.
|
|
187
204
|
|
|
188
|
-
{"nav": {"home": "Home"}}
|
|
205
|
+
{"nav": {"home": "Home"}} -> {"nav.home": "Home", "home": "Home"}
|
|
189
206
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
Two passes so the alias rule is correct:
|
|
208
|
+
1. Flatten to full dot-path keys only.
|
|
209
|
+
2. Add each leaf key as a shortcut ONLY if it is not already present.
|
|
210
|
+
|
|
211
|
+
So the FIRST dot-path wins on a leaf-key collision, and an explicit
|
|
212
|
+
top-level flat key is never overwritten by a derived alias. (The old
|
|
213
|
+
single-pass recursive merge was last-wins and could clobber an
|
|
214
|
+
explicit flat key -- silent data loss.)
|
|
194
215
|
"""
|
|
216
|
+
flat = I18n._flatten_paths(data, prefix)
|
|
217
|
+
result = dict(flat)
|
|
218
|
+
for full_key, value in flat.items():
|
|
219
|
+
leaf = full_key.rsplit(".", 1)[-1]
|
|
220
|
+
if leaf not in result:
|
|
221
|
+
result[leaf] = value
|
|
222
|
+
return result
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def _flatten_paths(data: dict, prefix: str = "") -> dict:
|
|
226
|
+
"""Flatten a nested dict to dot-path keys only (no leaf aliasing)."""
|
|
195
227
|
result = {}
|
|
196
228
|
for key, value in data.items():
|
|
197
229
|
full_key = f"{prefix}.{key}" if prefix else key
|
|
198
230
|
if isinstance(value, dict):
|
|
199
|
-
result.update(I18n.
|
|
231
|
+
result.update(I18n._flatten_paths(value, full_key))
|
|
200
232
|
else:
|
|
201
|
-
|
|
202
|
-
result[full_key] = str_value
|
|
203
|
-
# Also store the leaf key as a shortcut (first-wins on conflict)
|
|
204
|
-
if key not in result:
|
|
205
|
-
result[key] = str_value
|
|
233
|
+
result[full_key] = I18n._coerce_scalar(value)
|
|
206
234
|
return result
|
|
207
235
|
|
|
236
|
+
@staticmethod
|
|
237
|
+
def _coerce_scalar(value) -> str:
|
|
238
|
+
"""Render a non-string locale scalar JSON-natively (true/false/null)."""
|
|
239
|
+
if value is True:
|
|
240
|
+
return "true"
|
|
241
|
+
if value is False:
|
|
242
|
+
return "false"
|
|
243
|
+
if value is None:
|
|
244
|
+
return "null"
|
|
245
|
+
return str(value)
|
|
246
|
+
|
|
208
247
|
@staticmethod
|
|
209
248
|
def _resolve(key: str, translations: dict) -> str | None:
|
|
210
249
|
return translations.get(key)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";var Tina4=(()=>{var z=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var Je=Object.getOwnPropertyNames;var ze=Object.prototype.hasOwnProperty;var Ge=(e,n)=>{for(var t in n)z(e,t,{get:n[t],enumerable:!0})},Ve=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of Je(n))!ze.call(e,o)&&o!==t&&z(e,o,{get:()=>n[o],enumerable:!(r=Ue(n,o))||r.enumerable});return e};var Be=e=>Ve(z({},"__esModule",{value:!0}),e);var ht={};Ge(ht,{Tina4Element:()=>A,api:()=>Te,batch:()=>U,clearPersistedKeys:()=>De,computed:()=>de,createI18n:()=>ne,effect:()=>k,html:()=>pe,i18n:()=>qe,isSignal:()=>x,navigate:()=>J,persist:()=>Pe,pwa:()=>Re,route:()=>be,router:()=>Se,signal:()=>S,sse:()=>Ce,ws:()=>_e});var M=null,D=null,P=null,K=null;function _(e){K=e}function W(){return K}var le=null,ce=null,ue=[],Ze=512;var j=0,G=new Set;function S(e,n){let t=e,r=new Set,o={_t4:!0,get value(){if(M&&(r.add(M),D)){let s=M;D.push(()=>r.delete(s))}return t},set value(s){if(Object.is(s,t))return;let i=t;if(t=s,o._debugInfo&&o._debugInfo.updateCount++,ce&&ce(o,i,s),j>0)for(let a of r)G.add(a);else{let a;for(let c of[...r])try{c()}catch(u){a===void 0&&(a=u)}if(a!==void 0)throw a}},_subscribe(s){return r.add(s),()=>{r.delete(s)}},peek(){return t}};return le?(o._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:r},le(o,n)):ue.length<Ze&&ue.push({ref:new WeakRef(o),label:n,createdAt:Date.now(),subs:r}),o}function de(e){let n=S(void 0);return k(()=>{n.value=e()}),{_t4:!0,get value(){return n.value},set value(t){throw new Error("[tina4] computed signals are read-only")},_subscribe(t){return n._subscribe(t)},peek(){return n.peek()}}}function k(e){let n=!1,t=[],r=[],o=()=>{for(let a of r)a();r=[]},s=()=>{if(n)return;for(let m of t)m();t=[],o();let a=M,c=D,u=P;M=s,D=t,P=r;try{e()}finally{M=a,D=c,P=u}};s();let i=()=>{n=!0;for(let a of t)a();t=[],o()};return P&&P.push(i),K&&K.push(i),i}function U(e){j++;try{e()}finally{if(j--,j===0){let n=[...G];G.clear();let t;for(let r of n)try{r()}catch(o){t===void 0&&(t=o)}if(t!==void 0)throw t}}}function x(e){return e!==null&&typeof e=="object"&&e._t4===!0}var fe=new WeakMap,V="t4:";function pe(e,...n){let t=fe.get(e);if(!t){t=document.createElement("template");let i="";for(let a=0;a<e.length;a++)i+=e[a],a<n.length&&(tt(i)?i+=`__t4_${a}__`:i+=`<!--${V}${a}-->`);t.innerHTML=i,fe.set(e,t)}let r=t.content.cloneNode(!0),o=Qe(r);for(let{marker:i,index:a}of o)Ye(i,n[a]);let s=Xe(r);for(let i of s)et(i,n);return r}function Qe(e){let n=[];return Z(e,t=>{if(t.nodeType===8){let r=t.data;if(r&&r.startsWith(V)){let o=parseInt(r.slice(V.length),10);n.push({marker:t,index:o})}}}),n}function Xe(e){let n=[];return Z(e,t=>{t.nodeType===1&&n.push(t)}),n}function Z(e,n){let t=e.childNodes;for(let r=0;r<t.length;r++){let o=t[r];n(o),Z(o,n)}}function Ye(e,n){let t=e.parentNode;if(t)if(x(n)){let r=document.createTextNode("");t.replaceChild(r,e),k(()=>{r.data=String(n.value??"")})}else if(typeof n=="function"){let r=document.createComment("");t.replaceChild(r,e);let o=[],s=[];k(()=>{for(let y of s)y();s=[];let i=[],a=W();_(i);let c=n();_(a),s=i;for(let y of o)y.parentNode?.removeChild(y);o=[];let u=B(c),m=r.parentNode;if(m)for(let y of u)m.insertBefore(y,r),o.push(y)})}else if(ge(n))t.replaceChild(n,e);else if(n instanceof Node)t.replaceChild(n,e);else if(Array.isArray(n)){let r=document.createDocumentFragment();for(let o of n){let s=B(o);for(let i of s)r.appendChild(i)}t.replaceChild(r,e)}else{let r=document.createTextNode(String(n??""));t.replaceChild(r,e)}}function et(e,n){let t=[];for(let r of Array.from(e.attributes)){let o=r.name,s=r.value;if(o.startsWith("@")){let a=o.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let u=n[parseInt(c[1],10)];typeof u=="function"&&e.addEventListener(a,m=>U(()=>u(m)))}t.push(o);continue}if(o.startsWith("?")){let a=o.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let u=n[parseInt(c[1],10)];if(x(u)){let m=u;k(()=>{m.value?e.setAttribute(a,""):e.removeAttribute(a)})}else typeof u=="function"?k(()=>{u()?e.setAttribute(a,""):e.removeAttribute(a)}):u&&e.setAttribute(a,"")}t.push(o);continue}if(o.startsWith(".")){let a=o.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let u=n[parseInt(c[1],10)];x(u)?k(()=>{e[a]=u.value}):typeof u=="function"?k(()=>{e[a]=u()??""}):e[a]=u}t.push(o);continue}let i=s.match(/__t4_(\d+)__/);if(i){let a=n[parseInt(i[1],10)];if(x(a)){let c=a;k(()=>{e.setAttribute(o,String(c.value??""))})}else typeof a=="function"?k(()=>{e.setAttribute(o,String(a()??""))}):e.setAttribute(o,String(a??""))}}for(let r of t)e.removeAttribute(r)}function B(e){if(e==null||e===!1)return[];if(ge(e))return Array.from(e.childNodes);if(e instanceof Node)return[e];if(Array.isArray(e)){let n=[];for(let t of e)n.push(...B(t));return n}return[document.createTextNode(String(e))]}function ge(e){return e!=null&&typeof e=="object"&&e.nodeType===11}function tt(e){let n=!1,t=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="<"&&!n&&!t&&(r=!0),s===">"&&!n&&!t&&(r=!1),r&&(s==='"'&&!n&&(t=!t),s==="'"&&!t&&(n=!n))}return r}var me=null,he=null;var A=class extends HTMLElement{constructor(){super();this._props={};this._rendered=!1;this._disposeRender=null;this._innerDisposers=[];let t=this.constructor;this._root=t.shadow?this.attachShadow({mode:"open"}):this;for(let[r,o]of Object.entries(t.props))this._props[r]=S(this._coerce(this.getAttribute(r),o))}static get observedAttributes(){return Object.keys(this.props)}connectedCallback(){if(this._rendered)return;this._rendered=!0;let t=this.constructor,r=null;if(t.styles&&t.shadow&&this._root instanceof ShadowRoot){let o=document.createElement("style");o.textContent=t.styles,this._root.appendChild(o),r=o}this._disposeRender=k(()=>{this._innerDisposers.splice(0).forEach(c=>c());let o=[],s=W();_(o);let i=this.render();_(s),this._innerDisposers=o;let a=Array.from(this._root.childNodes);for(let c of a)c!==r&&this._root.removeChild(c);i&&this._root.appendChild(i)}),this.onMount(),me&&me(this)}disconnectedCallback(){this._disposeRender&&(this._disposeRender(),this._disposeRender=null),this._innerDisposers.splice(0).forEach(t=>t()),this.onUnmount(),he&&he(this)}attributeChangedCallback(t,r,o){let i=this.constructor.props[t];i&&this._props[t]&&(this._props[t].value=this._coerce(o,i))}prop(t){if(!this._props[t])throw new Error(`[tina4] Prop '${t}' not declared in static props of <${this.tagName.toLowerCase()}>`);return this._props[t]}emit(t,r){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,...r}))}onMount(){}onUnmount(){}_coerce(t,r){return r===Boolean?t!==null:r===Number?t!==null?Number(t):0:t??""}};A.props={},A.styles="",A.shadow=!0;var X=[],N=null,F="history",nt=!1,q=[],Q=[],ve=0;function be(e,n){let t=[],r;e==="*"?r=".*":r=e.replace(/\{(\w+)\}/g,(s,i)=>(t.push(i),"([^/]+)"));let o=new RegExp(`^${r}$`);typeof n=="function"?X.push({pattern:e,regex:o,paramNames:t,handler:n}):X.push({pattern:e,regex:o,paramNames:t,handler:n.handler,guard:n.guard})}function J(e,n){if(F==="hash")if(n?.replace){let t=new URL(location.href);t.hash="#"+e,history.replaceState(null,"",t.toString()),H()}else location.hash="#"+e;else n?.replace?history.replaceState(null,"",e):history.pushState(null,"",e),H()}function H(){if(!N)return;let e=performance.now(),n=++ve,t=F==="hash"?location.hash.slice(1)||"/":location.pathname;for(let r of X){let o=t.match(r.regex);if(!o)continue;let s={};if(r.paramNames.forEach((c,u)=>{s[c]=decodeURIComponent(o[u+1])}),r.guard){let c=r.guard();if(c===!1)return;if(typeof c=="string"){J(c,{replace:!0});return}}Q.splice(0).forEach(c=>c()),N.innerHTML="";let i=[];_(i);let a=r.handler(s);if(a instanceof Promise)a.then(c=>{if(_(null),n!==ve){for(let m of i)m();return}ye(N,c),Q=i;let u=performance.now()-e;for(let m of q)m({path:t,params:s,pattern:r.pattern,durationMs:u})});else{_(null),ye(N,a),Q=i;let c=performance.now()-e;for(let u of q)u({path:t,params:s,pattern:r.pattern,durationMs:c})}return}}function ye(e,n){n instanceof DocumentFragment||n instanceof Node?e.replaceChildren(n):typeof n=="string"?e.innerHTML=n:n!=null&&e.replaceChildren(document.createTextNode(String(n)))}var Se={start(e){if(N=document.querySelector(e.target),!N)throw new Error(`[tina4] Router target '${e.target}' not found in DOM`);F=e.mode??"history",nt=!0,window.addEventListener("popstate",H),F==="hash"&&window.addEventListener("hashchange",H),document.addEventListener("click",n=>{if(n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)return;let t=n.target.closest("a[href]");if(!t||t.origin!==location.origin||t.hasAttribute("target")||t.hasAttribute("download")||t.getAttribute("rel")?.includes("external"))return;n.preventDefault();let r=F==="hash"?t.getAttribute("href"):t.pathname;J(r)}),H()},on(e,n){return q.push(n),()=>{let t=q.indexOf(n);t>=0&&q.splice(t,1)}}};var E={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},Y=[],ee=[],rt=0;function te(){try{return localStorage.getItem(E.tokenKey)}catch{return null}}function ot(e){try{localStorage.setItem(E.tokenKey,e)}catch{}}function we(e,n){let t=Object.entries(n).map(([r,o])=>`${encodeURIComponent(r)}=${encodeURIComponent(String(o))}`).join("&");return e+(e.includes("?")?"&":"?")+t}async function ke(e,n){e._url=n,e._requestId=++rt;for(let a of Y){let c=a(e);c&&(e=c)}let t=await fetch(n,e),r=t.headers.get("FreshToken");r&&ot(r);let o=t.headers.get("Content-Type")??"",s;o.includes("json")?s=await t.json():s=await t.text();let i={status:t.status,data:s,ok:t.ok,headers:t.headers,_requestId:e._requestId};for(let a of ee){let c=a(i);c&&(i=c)}if(!t.ok)throw i;return i.data}async function L(e,n,t,r){let o={method:e,credentials:"same-origin",headers:{"Content-Type":"application/json",...E.headers}};if(E.auth){let s=te();s&&(o.headers.Authorization=`Bearer ${s}`)}if(t!==void 0&&e!=="GET"){let s=typeof t=="object"&&t!==null?{...t}:t;if(E.auth&&typeof s=="object"&&s!==null){let i=te();i&&(s.formToken=i)}o.body=JSON.stringify(s)}return r?.headers&&Object.assign(o.headers,r.headers),r?.params&&(n=we(n,r.params)),ke(o,E.baseUrl+n)}var Te={configure(e){Object.assign(E,e)},get(e,n){return L("GET",e,void 0,n)},post(e,n,t){return L("POST",e,n,t)},put(e,n,t){return L("PUT",e,n,t)},patch(e,n,t){return L("PATCH",e,n,t)},delete(e,n){return L("DELETE",e,void 0,n)},async graphql(e,n,t,r){return L("POST",e,{query:n,variables:t||{}},r)},async upload(e,n,t){let r={method:"POST",headers:{...E.headers},body:n};if(delete r.headers["Content-Type"],delete r.headers["content-type"],E.auth){let o=te();o&&(r.headers.Authorization=`Bearer ${o}`)}return t?.headers&&Object.assign(r.headers,t.headers),t?.params&&(e=we(e,t.params)),ke(r,E.baseUrl+e)},intercept(e,n){e==="request"?Y.push(n):ee.push(n)},_reset(){E.baseUrl="",E.auth=!1,E.tokenKey="tina4_token",E.headers={},Y.length=0,ee.length=0}};function st(e){let n=e.cacheStrategy??"network-first",t=JSON.stringify(e.precache??[]),r=e.offlineRoute?`'${e.offlineRoute}'`:"null";return`
|
|
2
|
+
const CACHE = 'tina4-v1';
|
|
3
|
+
const PRECACHE = ${t};
|
|
4
|
+
const OFFLINE = ${r};
|
|
5
|
+
|
|
6
|
+
self.addEventListener('install', (e) => {
|
|
7
|
+
e.waitUntil(
|
|
8
|
+
caches.open(CACHE).then((c) => c.addAll(PRECACHE)).then(() => self.skipWaiting())
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
self.addEventListener('activate', (e) => {
|
|
13
|
+
e.waitUntil(self.clients.claim());
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
self.addEventListener('fetch', (e) => {
|
|
17
|
+
const req = e.request;
|
|
18
|
+
if (req.method !== 'GET') return;
|
|
19
|
+
|
|
20
|
+
${n==="cache-first"?`
|
|
21
|
+
e.respondWith(
|
|
22
|
+
caches.match(req).then((cached) => cached || fetch(req).then((res) => {
|
|
23
|
+
const clone = res.clone();
|
|
24
|
+
caches.open(CACHE).then((c) => c.put(req, clone));
|
|
25
|
+
return res;
|
|
26
|
+
})).catch(() => OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
27
|
+
);`:n==="stale-while-revalidate"?`
|
|
28
|
+
e.respondWith(
|
|
29
|
+
caches.match(req).then((cached) => {
|
|
30
|
+
const fetched = fetch(req).then((res) => {
|
|
31
|
+
caches.open(CACHE).then((c) => c.put(req, res.clone()));
|
|
32
|
+
return res;
|
|
33
|
+
});
|
|
34
|
+
return cached || fetched;
|
|
35
|
+
}).catch(() => OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
36
|
+
);`:`
|
|
37
|
+
e.respondWith(
|
|
38
|
+
fetch(req).then((res) => {
|
|
39
|
+
const clone = res.clone();
|
|
40
|
+
caches.open(CACHE).then((c) => c.put(req, clone));
|
|
41
|
+
return res;
|
|
42
|
+
}).catch(() => caches.match(req).then((cached) =>
|
|
43
|
+
cached || (OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
44
|
+
))
|
|
45
|
+
);`}
|
|
46
|
+
});
|
|
47
|
+
`.trim()}function Ee(e){let n={name:e.name,short_name:e.shortName??e.name,start_url:"/",display:e.display??"standalone",background_color:e.backgroundColor??"#ffffff",theme_color:e.themeColor??"#000000"};return e.icon&&(n.icons=[{src:e.icon,sizes:"192x192",type:"image/png"},{src:e.icon,sizes:"512x512",type:"image/png"}]),n}var Re={register(e){let n=Ee(e),t=new Blob([JSON.stringify(n)],{type:"application/json"}),r=document.createElement("link");r.rel="manifest",r.href=URL.createObjectURL(t),document.head.appendChild(r);let o=document.querySelector('meta[name="theme-color"]');o||(o=document.createElement("meta"),o.name="theme-color",document.head.appendChild(o)),o.content=e.themeColor??"#000000","serviceWorker"in navigator&&(e.swUrl?navigator.serviceWorker.register(e.swUrl).catch(s=>{console.warn("[tina4] Service worker registration failed:",s)}):navigator.serviceWorker.register("/sw.js").catch(()=>{console.info("[tina4] No service worker at /sw.js. Use pwa.generateServiceWorker() to create one, or pass swUrl in config.")}))},generateServiceWorker(e){return st(e)},generateManifest(e){return Ee(e)}};var it={reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,protocols:[],token:""};function at(e){let n=Array.isArray(e.protocols)?e.protocols:e.protocols?[e.protocols]:[];return e.token?["bearer",e.token,...n]:e.protocols}function lt(e,n={}){let t={...it,...n},r=S("connecting"),o=S(!1),s=S(null),i=S(null),a=S(0),c={message:[],open:[],close:[],error:[]},u=null,m=!1,y=t.reconnectDelay,f=null,l=0;function p(g){if(typeof g!="string")return g;try{return JSON.parse(g)}catch{return g}}function d(){r.value=l>0?"reconnecting":"connecting";try{u=new WebSocket(e,at(t))}catch{r.value="closed",o.value=!1;return}u.onopen=()=>{r.value="open",o.value=!0,i.value=null,l=0,y=t.reconnectDelay,a.value=0;for(let g of c.open)g()},u.onmessage=g=>{let b=p(g.data);s.value=b;for(let R of c.message)R(b)},u.onclose=g=>{r.value="closed",o.value=!1;for(let b of c.close)b(g.code,g.reason);!m&&t.reconnect&&l<t.reconnectAttempts&&v()},u.onerror=g=>{i.value=g;for(let b of c.error)b(g)}}function v(){l++,a.value=l,r.value="reconnecting",f=setTimeout(()=>{f=null,d()},y),y=Math.min(y*2,t.reconnectMaxDelay)}let T={status:r,connected:o,lastMessage:s,error:i,reconnectCount:a,send(g){if(!u||u.readyState!==WebSocket.OPEN)throw new Error("[tina4] WebSocket is not connected");let b=typeof g=="string"?g:JSON.stringify(g);u.send(b)},on(g,b){return c[g].push(b),()=>{let R=c[g],I=R.indexOf(b);I>=0&&R.splice(I,1)}},pipe(g,b){let R=I=>{g.value=b(I,g.value)};return T.on("message",R)},close(g,b){m=!0,f&&(clearTimeout(f),f=null),u&&u.close(g??1e3,b??""),r.value="closed",o.value=!1}};return d(),T}var _e={connect:lt};var ct={mode:"eventsource",method:"GET",headers:{},body:void 0,reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,events:[],json:!0};function ut(e,n={}){let t={...ct,...n},r=S("connecting"),o=S(!1),s=S(null),i=S(null),a=S(null),c=S(0),u={message:[],open:[],close:[],error:[]},m=null,y=null,f=!1,l=t.reconnectDelay,p=null,d=0;function v(h){if(!t.json||typeof h!="string")return h;try{return JSON.parse(h)}catch{return h}}function T(h,w){s.value=h,i.value=w;for(let C of u.message)C(h,w??void 0)}function g(){r.value="open",o.value=!0,a.value=null,d=0,l=t.reconnectDelay,c.value=0;for(let h of u.open)h()}function b(){r.value="closed",o.value=!1;for(let h of u.close)h();!f&&t.reconnect&&d<t.reconnectAttempts&&$e()}function R(h){a.value=h;for(let w of u.error)w(h)}function I(){r.value=d>0?"reconnecting":"connecting";try{m=new EventSource(e)}catch{r.value="closed",o.value=!1;return}m.onopen=()=>g(),m.onmessage=h=>{T(v(h.data),null)};for(let h of t.events)m.addEventListener(h,w=>{T(v(w.data),h)});m.onerror=h=>{R(h),m&&m.readyState===2&&(m=null,b())}}function He(){r.value=d>0?"reconnecting":"connecting",y=new AbortController;let h={method:t.method,headers:t.headers,signal:y.signal};t.body!==void 0&&(h.body=typeof t.body=="string"?t.body:JSON.stringify(t.body)),fetch(e,h).then(async w=>{if(!w.ok){R(new Error(`[tina4] SSE fetch ${w.status}`)),b();return}g();let C=w.body.getReader(),O=new TextDecoder,$="";for(;;){let{done:je,value:Ke}=await C.read();if(je)break;$+=O.decode(Ke,{stream:!0});let ie=$.split(`
|
|
48
|
+
`);$=ie.pop();for(let We of ie){let ae=We.trim();ae&&T(v(ae),null)}}let se=$.trim();se&&T(v(se),null),y=null,b()}).catch(w=>{w.name!=="AbortError"&&(y=null,R(w),b())})}function $e(){d++,c.value=d,r.value="reconnecting",p=setTimeout(()=>{p=null,re()},l),l=Math.min(l*2,t.reconnectMaxDelay)}function re(){t.mode==="fetch"?He():I()}let oe={status:r,connected:o,lastMessage:s,lastEvent:i,error:a,reconnectCount:c,on(h,w){return u[h].push(w),()=>{let C=u[h],O=C.indexOf(w);O>=0&&C.splice(O,1)}},pipe(h,w){let C=O=>{h.value=w(O,h.value)};return oe.on("message",C)},close(){f=!0,p&&(clearTimeout(p),p=null),m&&(m.close(),m=null),y&&(y.abort(),y=null),r.value="closed",o.value=!1}};return re(),oe}var Ce={connect:ut};var xe={read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},Ae=/(token|password|passwd|secret|api[_-]?key|apikey|auth(?!or)|credential|jwt|bearer|otp|seed|private[_-]?key|session[_-]?id)/i,dt=/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}$/,ft=/^[A-Za-z0-9+/_=-]{40,}$/,Ie=new Set;function Oe(e,n){if(Ae.test(e))return`key name "${e}" looks like a credential`;if(typeof n=="string"){if(dt.test(n))return"value looks like a JWT";if(n.length>=40&&ft.test(n))return"value looks like a long base64 / token"}if(n&&typeof n=="object"&&!Array.isArray(n)){for(let t of Object.keys(n))if(Ae.test(t))return`object contains a credential-shape field "${t}"`}return null}function Me(e,n){Ie.has(n)||(Ie.add(n),console.warn(`[tina4 persist] ${e} (key: ${JSON.stringify(n)}). localStorage is XSS-readable and never appropriate for credentials, tokens, passwords, personal data, or secrets. See STORAGE.md.`))}function Le(e){if(typeof globalThis>"u")return null;try{let n=e==="local"?globalThis.localStorage:globalThis.sessionStorage;return!n||typeof n.getItem!="function"?null:n}catch{return null}}function Pe(e,n){let{key:t,storage:r="local",serializer:o=xe,version:s=1,migrate:i,syncTabs:a=!1,silenceCredentialWarning:c=!1}=n;if(!t||typeof t!="string")throw new Error("[tina4 persist] options.key is required and must be a string");let u=o===xe,m=Le(r);if(!m)return Ne(e,()=>{},()=>{});try{let l=m.getItem(t);if(l!==null){let p,d;try{let v=JSON.parse(l);v&&typeof v=="object"&&"value"in v?(p=v.v,d=v.value):d=v}catch{d=u?l:o.read(l)}if(p===s||p===void 0){let v=u?d:o.read(typeof d=="string"?d:JSON.stringify(d));e.value=v}else if(i)try{e.value=i(d,p)}catch(v){console.warn(`[tina4 persist] migrate() threw for key "${t}":`,v)}else console.warn(`[tina4 persist] stored version ${p} does not match current ${s} for key "${t}", and no migrate() was provided. Discarding the stored value.`)}}catch(l){console.warn(`[tina4 persist] failed to read key "${t}":`,l)}if(!c){let l=Oe(t,e.peek());l&&Me(l,t)}let y=k(()=>{let l=e.value;if(!c){let p=Oe(t,l);p&&Me(p,t)}try{let d=JSON.stringify(u?{v:s,value:l}:{v:s,value:o.write(l)});m.setItem(t,d)}catch(p){console.warn(`[tina4 persist] failed to write key "${t}":`,p)}}),f=null;if(a&&typeof globalThis<"u"&&"addEventListener"in globalThis){let l=p=>{let d=p;if(d.storageArea===m&&d.key===t&&d.newValue!==null)try{let v=JSON.parse(d.newValue),T=v&&typeof v=="object"&&"v"in v?v.v:void 0,g=T!==void 0?v.value:v;T!==void 0&&T!==s&&i?e.value=i(g,T):(T===s||T===void 0)&&(e.value=u?g:o.read(typeof g=="string"?g:JSON.stringify(g)))}catch(v){console.warn(`[tina4 persist] failed to parse storage event for key "${t}":`,v)}};globalThis.addEventListener?.("storage",l),f=()=>{globalThis.removeEventListener?.("storage",l)}}return Ne(e,()=>{try{m.removeItem(t)}catch(l){console.warn(`[tina4 persist] failed to clear key "${t}":`,l)}},()=>{y(),f&&f()})}function Ne(e,n,t){return Object.assign(e,{clear:n,dispose:t})}function De(e,n="local"){let t=Le(n);if(t)for(let r of e)try{t.removeItem(r)}catch(o){console.warn(`[tina4 persist] failed to clear key "${r}":`,o)}}var pt=["ar","he","fa","ur","ps","dv","syr","ckb","yi"];function gt(){return globalThis.navigator?.language||"en"}function Fe(e,n="",t={}){for(let[r,o]of Object.entries(e)){let s=n?`${n}.${r}`:r;if(o!==null&&typeof o=="object"&&!Array.isArray(o))Fe(o,s,t);else{let i=String(o);t[s]=i,r in t||(t[r]=i)}}return t}function mt(e,n){return e.replace(/\{(\w+)\}/g,(t,r)=>Object.prototype.hasOwnProperty.call(n,r)?String(n[r]):t)}function ne(e={}){let n=e.locale||gt(),t=e.fallbackLocale||n,r=new Set([...pt,...e.rtlLocales||[]]),o=S(n,"i18n.locale"),s=new Map,i=new Map;function a(f,l){let p=Fe(l),d=s.get(f);s.set(f,d?{...d,...p}:p)}if(e.messages)for(let[f,l]of Object.entries(e.messages))a(f,l);function c(f,l){return s.get(f)?.[l]}function u(f,l){let p=`n|${f}|${JSON.stringify(l||{})}`,d=i.get(p);return d||(d=new Intl.NumberFormat(f,l),i.set(p,d)),d}function m(f,l){let p=`d|${f}|${JSON.stringify(l||{})}`,d=i.get(p);return d||(d=new Intl.DateTimeFormat(f,l),i.set(p,d)),d}function y(f,l){let p=`r|${f}|${JSON.stringify(l||{})}`,d=i.get(p);return d||(d=new Intl.RelativeTimeFormat(f,l),i.set(p,d)),d}return{locale:o,t(f,l){let p=o.value,d=c(p,f);return d===void 0&&t!==p&&(d=c(t,f)),d===void 0&&(d=f),l?mt(d,l):d},setLocale(f){o.value=f},getLocale(){return o.value},addMessages:a,hasLocale(f){return s.has(f)},availableLocales(){return[...s.keys()].sort()},async loadMessages(f,l){let p=await fetch(l);if(!p.ok)throw new Error(`[tina4 i18n] failed to load "${f}" from ${l}: ${p.status}`);a(f,await p.json())},number(f,l){return u(o.value,l).format(f)},currency(f,l,p){return u(o.value,{style:"currency",currency:l,...p}).format(f)},date(f,l){let p=f instanceof Date?f:new Date(f);return m(o.value,l).format(p)},relativeTime(f,l,p){return y(o.value,p||{numeric:"auto"}).format(f,l)},isRTL(){return r.has(o.value.split("-")[0].toLowerCase())},dir(){return this.isRTL()?"rtl":"ltr"}}}var qe=ne();return Be(ht);})();
|
|
@@ -39,23 +39,11 @@ v3.13.42 — configurability for external/public APIs:
|
|
|
39
39
|
import json
|
|
40
40
|
import os
|
|
41
41
|
import re
|
|
42
|
-
import functools
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
# ── Decorators ─────────────────────────────────────────────────
|
|
46
45
|
# These attach metadata to route handlers for Swagger generation.
|
|
47
46
|
|
|
48
|
-
# Every swagger attr a decorator may need to carry forward when it wraps a
|
|
49
|
-
# handler that another decorator already annotated.
|
|
50
|
-
_SWAGGER_ATTRS = (
|
|
51
|
-
"_swagger_description", "_swagger_detail", "_swagger_params", "_swagger_query",
|
|
52
|
-
"_swagger_summary", "_swagger_tags", "_swagger_example", "_swagger_example_content_type",
|
|
53
|
-
"_swagger_example_response", "_swagger_example_responses", "_swagger_deprecated",
|
|
54
|
-
"_swagger_model", "_swagger_model_list",
|
|
55
|
-
"_swagger_security", "_swagger_request_schema", "_swagger_response_schemas",
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
47
|
# ── Configuration registry ─────────────────────────────────────
|
|
60
48
|
# Process-wide registries for security schemes and reusable component schemas
|
|
61
49
|
# declared programmatically (Swagger.add_security_scheme / Swagger.add_schema).
|
|
@@ -65,13 +53,6 @@ _REGISTERED_SCHEMES: dict[str, dict] = {}
|
|
|
65
53
|
_REGISTERED_SCHEMAS: dict[str, dict] = {}
|
|
66
54
|
|
|
67
55
|
|
|
68
|
-
def _carry(fn, wrapper):
|
|
69
|
-
"""Copy any already-set swagger attrs from fn onto wrapper (idempotent)."""
|
|
70
|
-
for attr in _SWAGGER_ATTRS:
|
|
71
|
-
if hasattr(fn, attr) and not hasattr(wrapper, attr):
|
|
72
|
-
setattr(wrapper, attr, getattr(fn, attr))
|
|
73
|
-
|
|
74
|
-
|
|
75
56
|
def description(text: str = "", detail: str = "", params: dict | None = None,
|
|
76
57
|
query: dict | None = None):
|
|
77
58
|
"""Add a description, optional detail body, and parameter docs to a route.
|
|
@@ -99,19 +80,12 @@ def description(text: str = "", detail: str = "", params: dict | None = None,
|
|
|
99
80
|
fn._swagger_params = params
|
|
100
81
|
if query:
|
|
101
82
|
fn._swagger_query = query
|
|
102
|
-
|
|
103
|
-
@
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
wrapper._swagger_detail = detail
|
|
109
|
-
if params:
|
|
110
|
-
wrapper._swagger_params = params
|
|
111
|
-
if query:
|
|
112
|
-
wrapper._swagger_query = query
|
|
113
|
-
_carry(fn, wrapper)
|
|
114
|
-
return wrapper
|
|
83
|
+
# No wrapper: annotate the handler in place and return the SAME object.
|
|
84
|
+
# @get is innermost and registers this exact object, so every stacked
|
|
85
|
+
# decorator must land its metadata here. A wrapper would be registered
|
|
86
|
+
# by nothing (the outer decorators never reach the router) and silently
|
|
87
|
+
# drop all metadata except the decorator adjacent to @get.
|
|
88
|
+
return fn
|
|
115
89
|
return decorator
|
|
116
90
|
|
|
117
91
|
|
|
@@ -119,12 +93,7 @@ def summary(text: str):
|
|
|
119
93
|
"""Add a short summary to a route handler."""
|
|
120
94
|
def decorator(fn):
|
|
121
95
|
fn._swagger_summary = text
|
|
122
|
-
|
|
123
|
-
def wrapper(*args, **kwargs):
|
|
124
|
-
return fn(*args, **kwargs)
|
|
125
|
-
wrapper._swagger_summary = text
|
|
126
|
-
_carry(fn, wrapper)
|
|
127
|
-
return wrapper
|
|
96
|
+
return fn
|
|
128
97
|
return decorator
|
|
129
98
|
|
|
130
99
|
|
|
@@ -140,12 +109,7 @@ def tags(tag_list):
|
|
|
140
109
|
|
|
141
110
|
def decorator(fn):
|
|
142
111
|
fn._swagger_tags = tag_list
|
|
143
|
-
|
|
144
|
-
def wrapper(*args, **kwargs):
|
|
145
|
-
return fn(*args, **kwargs)
|
|
146
|
-
wrapper._swagger_tags = tag_list
|
|
147
|
-
_carry(fn, wrapper)
|
|
148
|
-
return wrapper
|
|
112
|
+
return fn
|
|
149
113
|
return decorator
|
|
150
114
|
|
|
151
115
|
|
|
@@ -160,13 +124,7 @@ def example(data: dict | list, content_type: str = "application/json"):
|
|
|
160
124
|
def decorator(fn):
|
|
161
125
|
fn._swagger_example = data
|
|
162
126
|
fn._swagger_example_content_type = content_type
|
|
163
|
-
|
|
164
|
-
def wrapper(*args, **kwargs):
|
|
165
|
-
return fn(*args, **kwargs)
|
|
166
|
-
wrapper._swagger_example = data
|
|
167
|
-
wrapper._swagger_example_content_type = content_type
|
|
168
|
-
_carry(fn, wrapper)
|
|
169
|
-
return wrapper
|
|
127
|
+
return fn
|
|
170
128
|
return decorator
|
|
171
129
|
|
|
172
130
|
|
|
@@ -198,13 +156,7 @@ def example_response(status_or_data, data=None):
|
|
|
198
156
|
responses[status_code] = body
|
|
199
157
|
fn._swagger_example_responses = responses
|
|
200
158
|
fn._swagger_example_response = body
|
|
201
|
-
|
|
202
|
-
def wrapper(*args, **kwargs):
|
|
203
|
-
return fn(*args, **kwargs)
|
|
204
|
-
wrapper._swagger_example_responses = responses
|
|
205
|
-
wrapper._swagger_example_response = body
|
|
206
|
-
_carry(fn, wrapper)
|
|
207
|
-
return wrapper
|
|
159
|
+
return fn
|
|
208
160
|
return decorator
|
|
209
161
|
|
|
210
162
|
|
|
@@ -212,12 +164,7 @@ def deprecated():
|
|
|
212
164
|
"""Mark a route as deprecated."""
|
|
213
165
|
def decorator(fn):
|
|
214
166
|
fn._swagger_deprecated = True
|
|
215
|
-
|
|
216
|
-
def wrapper(*args, **kwargs):
|
|
217
|
-
return fn(*args, **kwargs)
|
|
218
|
-
wrapper._swagger_deprecated = True
|
|
219
|
-
_carry(fn, wrapper)
|
|
220
|
-
return wrapper
|
|
167
|
+
return fn
|
|
221
168
|
return decorator
|
|
222
169
|
|
|
223
170
|
|
|
@@ -257,12 +204,7 @@ def security(scheme_or_reqs="bearerAuth", scopes=None):
|
|
|
257
204
|
reqs = _normalize_security(scheme_or_reqs, scopes)
|
|
258
205
|
def decorator(fn):
|
|
259
206
|
fn._swagger_security = reqs
|
|
260
|
-
|
|
261
|
-
def wrapper(*args, **kwargs):
|
|
262
|
-
return fn(*args, **kwargs)
|
|
263
|
-
wrapper._swagger_security = reqs
|
|
264
|
-
_carry(fn, wrapper)
|
|
265
|
-
return wrapper
|
|
207
|
+
return fn
|
|
266
208
|
return decorator
|
|
267
209
|
|
|
268
210
|
|
|
@@ -274,12 +216,7 @@ def request_schema(name: str, content_type: str = "application/json"):
|
|
|
274
216
|
"""
|
|
275
217
|
def decorator(fn):
|
|
276
218
|
fn._swagger_request_schema = (name, content_type)
|
|
277
|
-
|
|
278
|
-
def wrapper(*args, **kwargs):
|
|
279
|
-
return fn(*args, **kwargs)
|
|
280
|
-
wrapper._swagger_request_schema = (name, content_type)
|
|
281
|
-
_carry(fn, wrapper)
|
|
282
|
-
return wrapper
|
|
219
|
+
return fn
|
|
283
220
|
return decorator
|
|
284
221
|
|
|
285
222
|
|
|
@@ -294,12 +231,7 @@ def response_schema(name: str, status: int = 200, is_list: bool = False):
|
|
|
294
231
|
existing = dict(getattr(fn, "_swagger_response_schemas", {}) or {})
|
|
295
232
|
existing[int(status)] = (name, bool(is_list))
|
|
296
233
|
fn._swagger_response_schemas = existing
|
|
297
|
-
|
|
298
|
-
def wrapper(*args, **kwargs):
|
|
299
|
-
return fn(*args, **kwargs)
|
|
300
|
-
wrapper._swagger_response_schemas = existing
|
|
301
|
-
_carry(fn, wrapper)
|
|
302
|
-
return wrapper
|
|
234
|
+
return fn
|
|
303
235
|
return decorator
|
|
304
236
|
|
|
305
237
|
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";var Tina4=(()=>{var B=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Ne=Object.prototype.hasOwnProperty;var Oe=(t,n)=>{for(var e in n)B(t,e,{get:n[e],enumerable:!0})},Ie=(t,n,e,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of qe(n))!Ne.call(t,r)&&r!==e&&B(t,r,{get:()=>n[r],enumerable:!(o=Me(n,r))||o.enumerable});return t};var De=t=>Ie(B({},"__esModule",{value:!0}),t);var Ve={};Oe(Ve,{Tina4Element:()=>W,api:()=>we,batch:()=>F,computed:()=>ce,effect:()=>b,html:()=>ue,isSignal:()=>R,navigate:()=>G,pwa:()=>Se,route:()=>me,router:()=>ve,signal:()=>v,sse:()=>Te,ws:()=>Ee});var q=null,I=null,j=null;function S(t){j=t}function U(){return j}var ae=null,ie=null;var K=0,z=new Set;function v(t,n){let e=t,o=new Set,r={_t4:!0,get value(){if(q&&(o.add(q),I)){let s=q;I.push(()=>o.delete(s))}return e},set value(s){if(Object.is(s,e))return;let i=e;if(e=s,r._debugInfo&&r._debugInfo.updateCount++,ie&&ie(r,i,s),K>0)for(let a of o)z.add(a);else{let a;for(let c of[...o])try{c()}catch(l){a===void 0&&(a=l)}if(a!==void 0)throw a}},_subscribe(s){return o.add(s),()=>{o.delete(s)}},peek(){return e}};return ae&&(r._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:o},ae(r,n)),r}function ce(t){let n=v(void 0);return b(()=>{n.value=t()}),{_t4:!0,get value(){return n.value},set value(e){throw new Error("[tina4] computed signals are read-only")},_subscribe(e){return n._subscribe(e)},peek(){return n.peek()}}}function b(t){let n=!1,e=[],o=()=>{if(n)return;for(let a of e)a();e=[];let s=q,i=I;q=o,I=e;try{t()}finally{q=s,I=i}};o();let r=()=>{n=!0;for(let s of e)s();e=[]};return j&&j.push(r),r}function F(t){K++;try{t()}finally{if(K--,K===0){let n=[...z];z.clear();let e;for(let o of n)try{o()}catch(r){e===void 0&&(e=r)}if(e!==void 0)throw e}}}function R(t){return t!==null&&typeof t=="object"&&t._t4===!0}var le=new WeakMap,Q="t4:";function ue(t,...n){let e=le.get(t);if(!e){e=document.createElement("template");let i="";for(let a=0;a<t.length;a++)i+=t[a],a<n.length&&(je(i)?i+=`__t4_${a}__`:i+=`<!--${Q}${a}-->`);e.innerHTML=i,le.set(t,e)}let o=e.content.cloneNode(!0),r=He(o);for(let{marker:i,index:a}of r)Pe(i,n[a]);let s=Le(o);for(let i of s)Ke(i,n);return o}function He(t){let n=[];return Y(t,e=>{if(e.nodeType===8){let o=e.data;if(o&&o.startsWith(Q)){let r=parseInt(o.slice(Q.length),10);n.push({marker:e,index:r})}}}),n}function Le(t){let n=[];return Y(t,e=>{e.nodeType===1&&n.push(e)}),n}function Y(t,n){let e=t.childNodes;for(let o=0;o<e.length;o++){let r=e[o];n(r),Y(r,n)}}function Pe(t,n){let e=t.parentNode;if(e)if(R(n)){let o=document.createTextNode("");e.replaceChild(o,t),b(()=>{o.data=String(n.value??"")})}else if(typeof n=="function"){let o=document.createComment("");e.replaceChild(o,t);let r=[],s=[];b(()=>{for(let u of s)u();s=[];let i=[],a=U();S(i);let c=n();S(a),s=i;for(let u of r)u.parentNode?.removeChild(u);r=[];let l=X(c),d=o.parentNode;if(d)for(let u of l)d.insertBefore(u,o),r.push(u)})}else if(de(n))e.replaceChild(n,t);else if(n instanceof Node)e.replaceChild(n,t);else if(Array.isArray(n)){let o=document.createDocumentFragment();for(let r of n){let s=X(r);for(let i of s)o.appendChild(i)}e.replaceChild(o,t)}else{let o=document.createTextNode(String(n??""));e.replaceChild(o,t)}}function Ke(t,n){let e=[];for(let o of Array.from(t.attributes)){let r=o.name,s=o.value;if(r.startsWith("@")){let a=r.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];typeof l=="function"&&t.addEventListener(a,d=>F(()=>l(d)))}e.push(r);continue}if(r.startsWith("?")){let a=r.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];if(R(l)){let d=l;b(()=>{d.value?t.setAttribute(a,""):t.removeAttribute(a)})}else typeof l=="function"?b(()=>{l()?t.setAttribute(a,""):t.removeAttribute(a)}):l&&t.setAttribute(a,"")}e.push(r);continue}if(r.startsWith(".")){let a=r.slice(1),c=s.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];R(l)?b(()=>{t[a]=l.value}):t[a]=l}e.push(r);continue}let i=s.match(/__t4_(\d+)__/);if(i){let a=n[parseInt(i[1],10)];if(R(a)){let c=a;b(()=>{t.setAttribute(r,String(c.value??""))})}else typeof a=="function"?b(()=>{t.setAttribute(r,String(a()??""))}):t.setAttribute(r,String(a??""))}}for(let o of e)t.removeAttribute(o)}function X(t){if(t==null||t===!1)return[];if(de(t))return Array.from(t.childNodes);if(t instanceof Node)return[t];if(Array.isArray(t)){let n=[];for(let e of t)n.push(...X(e));return n}return[document.createTextNode(String(t))]}function de(t){return t!=null&&typeof t=="object"&&t.nodeType===11}function je(t){let n=!1,e=!1,o=!1;for(let r=0;r<t.length;r++){let s=t[r];s==="<"&&!n&&!e&&(o=!0),s===">"&&!n&&!e&&(o=!1),o&&(s==='"'&&!n&&(e=!e),s==="'"&&!e&&(n=!n))}return o}var fe=null,pe=null;var W=class extends HTMLElement{constructor(){super();this._props={};this._rendered=!1;this._disposeRender=null;this._innerDisposers=[];let e=this.constructor;this._root=e.shadow?this.attachShadow({mode:"open"}):this;for(let[o,r]of Object.entries(e.props))this._props[o]=v(this._coerce(this.getAttribute(o),r))}static{this.props={}}static{this.styles=""}static{this.shadow=!0}static get observedAttributes(){return Object.keys(this.props)}connectedCallback(){if(this._rendered)return;this._rendered=!0;let e=this.constructor,o=null;if(e.styles&&e.shadow&&this._root instanceof ShadowRoot){let r=document.createElement("style");r.textContent=e.styles,this._root.appendChild(r),o=r}this._disposeRender=b(()=>{for(let c of this._innerDisposers)c();this._innerDisposers=[];let r=[],s=U();S(r);let i=this.render();S(s),this._innerDisposers=r;let a=Array.from(this._root.childNodes);for(let c of a)c!==o&&this._root.removeChild(c);i&&this._root.appendChild(i)}),this.onMount(),fe&&fe(this)}disconnectedCallback(){this._disposeRender&&(this._disposeRender(),this._disposeRender=null);for(let e of this._innerDisposers)e();this._innerDisposers=[],this.onUnmount(),pe&&pe(this)}attributeChangedCallback(e,o,r){let i=this.constructor.props[e];i&&this._props[e]&&(this._props[e].value=this._coerce(r,i))}prop(e){if(!this._props[e])throw new Error(`[tina4] Prop '${e}' not declared in static props of <${this.tagName.toLowerCase()}>`);return this._props[e]}emit(e,o){this.dispatchEvent(new CustomEvent(e,{bubbles:!0,composed:!0,...o}))}onMount(){}onUnmount(){}_coerce(e,o){return o===Boolean?e!==null:o===Number?e!==null?Number(e):0:e??""}};var Z=[],N=null,D="history",Ue=!1,H=[],$=[],ge=0;function me(t,n){let e=[],o;t==="*"?o=".*":o=t.replace(/\{(\w+)\}/g,(s,i)=>(e.push(i),"([^/]+)"));let r=new RegExp(`^${o}$`);typeof n=="function"?Z.push({pattern:t,regex:r,paramNames:e,handler:n}):Z.push({pattern:t,regex:r,paramNames:e,handler:n.handler,guard:n.guard})}function G(t,n){if(D==="hash")if(n?.replace){let e=new URL(location.href);e.hash="#"+t,history.replaceState(null,"",e.toString()),L()}else location.hash="#"+t;else n?.replace?history.replaceState(null,"",t):history.pushState(null,"",t),L()}function L(){if(!N)return;let t=performance.now(),n=++ge,e=D==="hash"?location.hash.slice(1)||"/":location.pathname;for(let o of Z){let r=e.match(o.regex);if(!r)continue;let s={};if(o.paramNames.forEach((c,l)=>{s[c]=decodeURIComponent(r[l+1])}),o.guard){let c=o.guard();if(c===!1)return;if(typeof c=="string"){G(c,{replace:!0});return}}for(let c of $)c();$=[],N.innerHTML="";let i=[];S(i);let a=o.handler(s);if(a instanceof Promise)a.then(c=>{if(S(null),n!==ge){for(let d of i)d();return}he(N,c),$=i;let l=performance.now()-t;for(let d of H)d({path:e,params:s,pattern:o.pattern,durationMs:l})});else{S(null),he(N,a),$=i;let c=performance.now()-t;for(let l of H)l({path:e,params:s,pattern:o.pattern,durationMs:c})}return}}function he(t,n){n instanceof DocumentFragment||n instanceof Node?t.replaceChildren(n):typeof n=="string"?t.innerHTML=n:n!=null&&t.replaceChildren(document.createTextNode(String(n)))}var ve={start(t){if(N=document.querySelector(t.target),!N)throw new Error(`[tina4] Router target '${t.target}' not found in DOM`);D=t.mode??"history",Ue=!0,window.addEventListener("popstate",L),D==="hash"&&window.addEventListener("hashchange",L),document.addEventListener("click",n=>{if(n.metaKey||n.ctrlKey||n.shiftKey||n.altKey)return;let e=n.target.closest("a[href]");if(!e||e.origin!==location.origin||e.hasAttribute("target")||e.hasAttribute("download")||e.getAttribute("rel")?.includes("external"))return;n.preventDefault();let o=D==="hash"?e.getAttribute("href"):e.pathname;G(o)}),L()},on(t,n){return H.push(n),()=>{let e=H.indexOf(n);e>=0&&H.splice(e,1)}}};var y={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},J=[],V=[],ye=0;function ee(){try{return localStorage.getItem(y.tokenKey)}catch{return null}}function be(t){try{localStorage.setItem(y.tokenKey,t)}catch{}}async function O(t,n,e,o){let r={method:t,credentials:"same-origin",headers:{"Content-Type":"application/json",...y.headers}};if(y.auth){let u=ee();u&&(r.headers.Authorization=`Bearer ${u}`)}if(e!==void 0&&t!=="GET"){let u=typeof e=="object"&&e!==null?{...e}:e;if(y.auth&&typeof u=="object"&&u!==null){let g=ee();g&&(u.formToken=g)}r.body=JSON.stringify(u)}if(o?.headers&&Object.assign(r.headers,o.headers),o?.params){let u=Object.entries(o.params).map(([g,w])=>`${encodeURIComponent(g)}=${encodeURIComponent(String(w))}`).join("&");n+=(n.includes("?")?"&":"?")+u}let s=y.baseUrl+n;r._url=s,r._requestId=++ye;for(let u of J){let g=u(r);g&&(r=g)}let i=await fetch(s,r),a=i.headers.get("FreshToken");a&&be(a);let c=i.headers.get("Content-Type")??"",l;c.includes("json")?l=await i.json():l=await i.text();let d={status:i.status,data:l,ok:i.ok,headers:i.headers,_requestId:r._requestId};for(let u of V){let g=u(d);g&&(d=g)}if(!i.ok)throw d;return d.data}var we={configure(t){Object.assign(y,t)},get(t,n){return O("GET",t,void 0,n)},post(t,n,e){return O("POST",t,n,e)},put(t,n,e){return O("PUT",t,n,e)},patch(t,n,e){return O("PATCH",t,n,e)},delete(t,n){return O("DELETE",t,void 0,n)},async graphql(t,n,e,o){return O("POST",t,{query:n,variables:e||{}},o)},async upload(t,n,e){let o={method:"POST",headers:{...y.headers},body:n};if(delete o.headers["Content-Type"],delete o.headers["content-type"],y.auth){let d=ee();d&&(o.headers.Authorization=`Bearer ${d}`)}if(e?.headers&&Object.assign(o.headers,e.headers),e?.params){let d=Object.entries(e.params).map(([u,g])=>`${encodeURIComponent(u)}=${encodeURIComponent(String(g))}`).join("&");t+=(t.includes("?")?"&":"?")+d}let r=y.baseUrl+t;o._url=r,o._requestId=++ye;for(let d of J){let u=d(o);u&&(o=u)}let s=await fetch(r,o),i=s.headers.get("FreshToken");i&&be(i);let a=s.headers.get("Content-Type")??"",c;a.includes("json")?c=await s.json():c=await s.text();let l={status:s.status,data:c,ok:s.ok,headers:s.headers,_requestId:o._requestId};for(let d of V){let u=d(l);u&&(l=u)}if(!s.ok)throw l;return l.data},intercept(t,n){t==="request"?J.push(n):V.push(n)},_reset(){y.baseUrl="",y.auth=!1,y.tokenKey="tina4_token",y.headers={},J.length=0,V.length=0}};function Fe(t){let n=t.cacheStrategy??"network-first",e=JSON.stringify(t.precache??[]),o=t.offlineRoute?`'${t.offlineRoute}'`:"null";return`
|
|
2
|
-
const CACHE = 'tina4-v1';
|
|
3
|
-
const PRECACHE = ${e};
|
|
4
|
-
const OFFLINE = ${o};
|
|
5
|
-
|
|
6
|
-
self.addEventListener('install', (e) => {
|
|
7
|
-
e.waitUntil(
|
|
8
|
-
caches.open(CACHE).then((c) => c.addAll(PRECACHE)).then(() => self.skipWaiting())
|
|
9
|
-
);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
self.addEventListener('activate', (e) => {
|
|
13
|
-
e.waitUntil(self.clients.claim());
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
self.addEventListener('fetch', (e) => {
|
|
17
|
-
const req = e.request;
|
|
18
|
-
if (req.method !== 'GET') return;
|
|
19
|
-
|
|
20
|
-
${n==="cache-first"?`
|
|
21
|
-
e.respondWith(
|
|
22
|
-
caches.match(req).then((cached) => cached || fetch(req).then((res) => {
|
|
23
|
-
const clone = res.clone();
|
|
24
|
-
caches.open(CACHE).then((c) => c.put(req, clone));
|
|
25
|
-
return res;
|
|
26
|
-
})).catch(() => OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
27
|
-
);`:n==="stale-while-revalidate"?`
|
|
28
|
-
e.respondWith(
|
|
29
|
-
caches.match(req).then((cached) => {
|
|
30
|
-
const fetched = fetch(req).then((res) => {
|
|
31
|
-
caches.open(CACHE).then((c) => c.put(req, res.clone()));
|
|
32
|
-
return res;
|
|
33
|
-
});
|
|
34
|
-
return cached || fetched;
|
|
35
|
-
}).catch(() => OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
36
|
-
);`:`
|
|
37
|
-
e.respondWith(
|
|
38
|
-
fetch(req).then((res) => {
|
|
39
|
-
const clone = res.clone();
|
|
40
|
-
caches.open(CACHE).then((c) => c.put(req, clone));
|
|
41
|
-
return res;
|
|
42
|
-
}).catch(() => caches.match(req).then((cached) =>
|
|
43
|
-
cached || (OFFLINE ? caches.match(OFFLINE) : new Response('Offline', { status: 503 }))
|
|
44
|
-
))
|
|
45
|
-
);`}
|
|
46
|
-
});
|
|
47
|
-
`.trim()}function ke(t){let n={name:t.name,short_name:t.shortName??t.name,start_url:"/",display:t.display??"standalone",background_color:t.backgroundColor??"#ffffff",theme_color:t.themeColor??"#000000"};return t.icon&&(n.icons=[{src:t.icon,sizes:"192x192",type:"image/png"},{src:t.icon,sizes:"512x512",type:"image/png"}]),n}var Se={register(t){let n=ke(t),e=new Blob([JSON.stringify(n)],{type:"application/json"}),o=document.createElement("link");o.rel="manifest",o.href=URL.createObjectURL(e),document.head.appendChild(o);let r=document.querySelector('meta[name="theme-color"]');r||(r=document.createElement("meta"),r.name="theme-color",document.head.appendChild(r)),r.content=t.themeColor??"#000000","serviceWorker"in navigator&&(t.swUrl?navigator.serviceWorker.register(t.swUrl).catch(s=>{console.warn("[tina4] Service worker registration failed:",s)}):navigator.serviceWorker.register("/sw.js").catch(()=>{console.info("[tina4] No service worker at /sw.js. Use pwa.generateServiceWorker() to create one, or pass swUrl in config.")}))},generateServiceWorker(t){return Fe(t)},generateManifest(t){return ke(t)}};var We={reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,protocols:[]};function $e(t,n={}){let e={...We,...n},o=v("connecting"),r=v(!1),s=v(null),i=v(null),a=v(0),c={message:[],open:[],close:[],error:[]},l=null,d=!1,u=e.reconnectDelay,g=null,w=0;function C(p){if(typeof p!="string")return p;try{return JSON.parse(p)}catch{return p}}function E(){o.value=w>0?"reconnecting":"connecting";try{l=new WebSocket(t,e.protocols)}catch{o.value="closed",r.value=!1;return}l.onopen=()=>{o.value="open",r.value=!0,i.value=null,w=0,u=e.reconnectDelay,a.value=0;for(let p of c.open)p()},l.onmessage=p=>{let h=C(p.data);s.value=h;for(let k of c.message)k(h)},l.onclose=p=>{o.value="closed",r.value=!1;for(let h of c.close)h(p.code,p.reason);!d&&e.reconnect&&w<e.reconnectAttempts&&x()},l.onerror=p=>{i.value=p;for(let h of c.error)h(p)}}function x(){w++,a.value=w,o.value="reconnecting",g=setTimeout(()=>{g=null,E()},u),u=Math.min(u*2,e.reconnectMaxDelay)}let _={status:o,connected:r,lastMessage:s,error:i,reconnectCount:a,send(p){if(!l||l.readyState!==WebSocket.OPEN)throw new Error("[tina4] WebSocket is not connected");let h=typeof p=="string"?p:JSON.stringify(p);l.send(h)},on(p,h){return c[p].push(h),()=>{let k=c[p],A=k.indexOf(h);A>=0&&k.splice(A,1)}},pipe(p,h){let k=A=>{p.value=h(A,p.value)};return _.on("message",k)},close(p,h){d=!0,g&&(clearTimeout(g),g=null),l&&l.close(p??1e3,h??""),o.value="closed",r.value=!1}};return E(),_}var Ee={connect:$e};var Ge={mode:"eventsource",method:"GET",headers:{},body:void 0,reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,events:[],json:!0};function Je(t,n={}){let e={...Ge,...n},o=v("connecting"),r=v(!1),s=v(null),i=v(null),a=v(null),c=v(0),l={message:[],open:[],close:[],error:[]},d=null,u=null,g=!1,w=e.reconnectDelay,C=null,E=0;function x(f){if(!e.json||typeof f!="string")return f;try{return JSON.parse(f)}catch{return f}}function _(f,m){s.value=f,i.value=m;for(let T of l.message)T(f,m??void 0)}function p(){o.value="open",r.value=!0,a.value=null,E=0,w=e.reconnectDelay,c.value=0;for(let f of l.open)f()}function h(){o.value="closed",r.value=!1;for(let f of l.close)f();!g&&e.reconnect&&E<e.reconnectAttempts&&_e()}function k(f){a.value=f;for(let m of l.error)m(f)}function A(){o.value=E>0?"reconnecting":"connecting";try{d=new EventSource(t)}catch{o.value="closed",r.value=!1;return}d.onopen=()=>p(),d.onmessage=f=>{_(x(f.data),null)};for(let f of e.events)d.addEventListener(f,m=>{_(x(m.data),f)});d.onerror=f=>{k(f),d&&d.readyState===2&&(d=null,h())}}function Ce(){o.value=E>0?"reconnecting":"connecting",u=new AbortController;let f={method:e.method,headers:e.headers,signal:u.signal};e.body!==void 0&&(f.body=typeof e.body=="string"?e.body:JSON.stringify(e.body)),fetch(t,f).then(async m=>{if(!m.ok){k(new Error(`[tina4] SSE fetch ${m.status}`)),h();return}p();let T=m.body.getReader(),M=new TextDecoder,P="";for(;;){let{done:Re,value:xe}=await T.read();if(Re)break;P+=M.decode(xe,{stream:!0});let re=P.split(`
|
|
48
|
-
`);P=re.pop();for(let Ae of re){let se=Ae.trim();se&&_(x(se),null)}}let oe=P.trim();oe&&_(x(oe),null),u=null,h()}).catch(m=>{m.name!=="AbortError"&&(u=null,k(m),h())})}function _e(){E++,c.value=E,o.value="reconnecting",C=setTimeout(()=>{C=null,te()},w),w=Math.min(w*2,e.reconnectMaxDelay)}function te(){e.mode==="fetch"?Ce():A()}let ne={status:o,connected:r,lastMessage:s,lastEvent:i,error:a,reconnectCount:c,on(f,m){return l[f].push(m),()=>{let T=l[f],M=T.indexOf(m);M>=0&&T.splice(M,1)}},pipe(f,m){let T=M=>{f.value=m(M,f.value)};return ne.on("message",T)},close(){g=!0,C&&(clearTimeout(C),C=null),d&&(d.close(),d=null),u&&(u.abort(),u=null),o.value="closed",r.value=!1}};return te(),ne}var Te={connect:Je};return De(Ve);})();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.47 → tina4_python-3.13.49}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|