tina4-python 3.3.0__tar.gz → 3.5.0__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.
Files changed (135) hide show
  1. {tina4_python-3.3.0 → tina4_python-3.5.0}/PKG-INFO +1 -1
  2. {tina4_python-3.3.0 → tina4_python-3.5.0}/pyproject.toml +1 -1
  3. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/crud/__init__.py +40 -0
  5. tina4_python-3.5.0/tina4_python/public/js/tina4js.min.js +47 -0
  6. {tina4_python-3.3.0 → tina4_python-3.5.0}/.gitignore +0 -0
  7. {tina4_python-3.3.0 → tina4_python-3.5.0}/README.md +0 -0
  8. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/CLAUDE.md +0 -0
  9. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/HtmlElement.py +0 -0
  10. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/Testing.py +0 -0
  11. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/ai/__init__.py +0 -0
  12. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/api/__init__.py +0 -0
  13. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/auth/__init__.py +0 -0
  14. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/cache/__init__.py +0 -0
  15. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/cli/__init__.py +0 -0
  16. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/container/__init__.py +0 -0
  17. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/__init__.py +0 -0
  18. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/cache.py +0 -0
  19. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/constants.py +0 -0
  20. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/events.py +0 -0
  21. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/middleware.py +0 -0
  22. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/request.py +0 -0
  23. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/response.py +0 -0
  24. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/core/server.py +0 -0
  26. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/__init__.py +0 -0
  27. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/adapter.py +0 -0
  28. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/connection.py +0 -0
  29. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/mssql.py +0 -0
  31. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/mysql.py +0 -0
  32. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/odbc.py +0 -0
  33. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/postgres.py +0 -0
  34. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/database/sqlite.py +0 -0
  35. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/debug/__init__.py +0 -0
  36. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/debug/error_overlay.py +0 -0
  37. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/dev_admin/__init__.py +0 -0
  38. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/dev_reload.py +0 -0
  39. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/dotenv/__init__.py +0 -0
  40. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/frond/FROND.md +0 -0
  41. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/frond/__init__.py +0 -0
  42. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/frond/engine.py +0 -0
  43. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/auth/meta.json +0 -0
  44. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  45. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/database/meta.json +0 -0
  46. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  47. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/error-overlay/meta.json +0 -0
  48. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  49. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/orm/meta.json +0 -0
  50. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  51. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  52. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/queue/meta.json +0 -0
  53. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  54. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/rest-api/meta.json +0 -0
  55. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  56. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/templates/meta.json +0 -0
  57. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  58. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  59. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/graphql/__init__.py +0 -0
  60. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/i18n/__init__.py +0 -0
  61. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/messenger/__init__.py +0 -0
  62. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/migration/__init__.py +0 -0
  63. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/migration/runner.py +0 -0
  64. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/orm/__init__.py +0 -0
  65. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/orm/fields.py +0 -0
  66. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/orm/model.py +0 -0
  67. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/css/tina4.css +0 -0
  68. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/css/tina4.min.css +0 -0
  69. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/favicon.ico +0 -0
  70. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/images/logo.svg +0 -0
  71. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  72. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/js/frond.min.js +0 -0
  73. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  74. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/js/tina4.min.js +0 -0
  75. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/swagger/index.html +0 -0
  76. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  77. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/queue/__init__.py +0 -0
  78. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/queue_backends/__init__.py +0 -0
  79. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/queue_backends/kafka_backend.py +0 -0
  80. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/queue_backends/mongo_backend.py +0 -0
  81. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  82. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/__init__.py +0 -0
  83. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  84. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_badges.scss +0 -0
  85. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  86. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_cards.scss +0 -0
  87. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_forms.scss +0 -0
  88. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_grid.scss +0 -0
  89. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_modals.scss +0 -0
  90. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_nav.scss +0 -0
  91. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_reset.scss +0 -0
  92. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_tables.scss +0 -0
  93. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_typography.scss +0 -0
  94. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  95. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/_variables.scss +0 -0
  96. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/base.scss +0 -0
  97. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/colors.scss +0 -0
  98. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/scss/tina4css/tina4.scss +0 -0
  99. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/seeder/__init__.py +0 -0
  100. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/service/__init__.py +0 -0
  101. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/session/__init__.py +0 -0
  102. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/session_handlers/__init__.py +0 -0
  103. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  104. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/session_handlers/redis_handler.py +0 -0
  105. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/session_handlers/valkey_handler.py +0 -0
  106. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/swagger/__init__.py +0 -0
  107. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/components/crud.twig +0 -0
  108. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  109. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  110. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/docker/python/Dockerfile +0 -0
  111. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  112. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/302.twig +0 -0
  113. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/401.twig +0 -0
  114. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/403.twig +0 -0
  115. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/404.twig +0 -0
  116. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/500.twig +0 -0
  117. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/502.twig +0 -0
  118. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/503.twig +0 -0
  119. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/errors/base.twig +0 -0
  120. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/frontend/README.md +0 -0
  121. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/templates/readme.md +0 -0
  122. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  123. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  124. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  125. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  126. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  127. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  128. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  129. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  130. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  131. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  132. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  133. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  134. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/websocket/__init__.py +0 -0
  135. {tina4_python-3.3.0 → tina4_python-3.5.0}/tina4_python/wsdl/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.3.0
3
+ Version: 3.5.0
4
4
  Summary: Tina4 Python v3 — Zero-dependency, lightweight web framework
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "3.3.0"
3
+ version = "3.5.0"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -8,7 +8,7 @@ Tina4 Python v3.0 — Zero-dependency, lightweight web framework.
8
8
 
9
9
  One import, everything works.
10
10
  """
11
- __version__ = "3.3.0"
11
+ __version__ = "3.5.0"
12
12
 
13
13
  # ── HTTP Constants ──
14
14
  from tina4_python.core.constants import ( # noqa: E402, F401
@@ -39,6 +39,32 @@ class AutoCrud:
39
39
  # Track registered models for introspection
40
40
  _registered: dict[str, type] = {}
41
41
 
42
+ @staticmethod
43
+ def _build_example(model_class) -> dict:
44
+ """Build a sample request body from ORM field definitions.
45
+
46
+ Generates a dict with field names as keys and example values
47
+ based on field types, suitable for Swagger request body examples.
48
+ """
49
+ from datetime import datetime
50
+
51
+ example = {}
52
+ for name, field in model_class._fields.items():
53
+ if field.primary_key and field.auto_increment:
54
+ continue # Skip auto-generated PKs
55
+ ft = field.field_type
56
+ if ft == int:
57
+ example[name] = 0
58
+ elif ft == float:
59
+ example[name] = 0.0
60
+ elif ft == bool:
61
+ example[name] = True
62
+ elif ft == datetime:
63
+ example[name] = "2024-01-01T00:00:00"
64
+ else:
65
+ example[name] = "string"
66
+ return example
67
+
42
68
  @staticmethod
43
69
  def register(model_class, prefix: str = "/api"):
44
70
  """Register REST endpoints for a single ORM model class.
@@ -62,6 +88,8 @@ class AutoCrud:
62
88
  base_path = f"{prefix}/{table}"
63
89
  pk_field = model_class._get_pk()
64
90
  generated = []
91
+ pretty_name = table.replace("_", " ").title()
92
+ example_body = AutoCrud._build_example(model_class)
65
93
 
66
94
  # ── GET /api/{table} — list with pagination ──────────────
67
95
  async def list_handler(request, response, _cls=model_class):
@@ -82,6 +110,8 @@ class AutoCrud:
82
110
 
83
111
  list_handler.__name__ = f"autocrud_list_{table}"
84
112
  list_handler.__qualname__ = f"autocrud_list_{table}"
113
+ list_handler._swagger_summary = f"List all {pretty_name}"
114
+ list_handler._swagger_tags = [table]
85
115
  Router.add("GET", base_path, list_handler)
86
116
  generated.append({"method": "GET", "path": base_path, "table": table})
87
117
 
@@ -95,6 +125,8 @@ class AutoCrud:
95
125
 
96
126
  get_handler.__name__ = f"autocrud_get_{table}"
97
127
  get_handler.__qualname__ = f"autocrud_get_{table}"
128
+ get_handler._swagger_summary = f"Get {pretty_name} by ID"
129
+ get_handler._swagger_tags = [table]
98
130
  Router.add("GET", f"{base_path}/{{id}}", get_handler)
99
131
  generated.append({"method": "GET", "path": f"{base_path}/{{id}}", "table": table})
100
132
 
@@ -114,6 +146,9 @@ class AutoCrud:
114
146
  create_handler.__name__ = f"autocrud_create_{table}"
115
147
  create_handler.__qualname__ = f"autocrud_create_{table}"
116
148
  create_handler._noauth = True
149
+ create_handler._swagger_summary = f"Create {pretty_name}"
150
+ create_handler._swagger_tags = [table]
151
+ create_handler._swagger_example = example_body
117
152
  Router.add("POST", base_path, create_handler)
118
153
  generated.append({"method": "POST", "path": base_path, "table": table})
119
154
 
@@ -143,6 +178,9 @@ class AutoCrud:
143
178
  update_handler.__name__ = f"autocrud_update_{table}"
144
179
  update_handler.__qualname__ = f"autocrud_update_{table}"
145
180
  update_handler._noauth = True
181
+ update_handler._swagger_summary = f"Update {pretty_name}"
182
+ update_handler._swagger_tags = [table]
183
+ update_handler._swagger_example = example_body
146
184
  Router.add("PUT", f"{base_path}/{{id}}", update_handler)
147
185
  generated.append({"method": "PUT", "path": f"{base_path}/{{id}}", "table": table})
148
186
 
@@ -162,6 +200,8 @@ class AutoCrud:
162
200
  delete_handler.__name__ = f"autocrud_delete_{table}"
163
201
  delete_handler.__qualname__ = f"autocrud_delete_{table}"
164
202
  delete_handler._noauth = True
203
+ delete_handler._swagger_summary = f"Delete {pretty_name}"
204
+ delete_handler._swagger_tags = [table]
165
205
  Router.add("DELETE", f"{base_path}/{{id}}", delete_handler)
166
206
  generated.append({"method": "DELETE", "path": f"{base_path}/{{id}}", "table": table})
167
207
 
@@ -0,0 +1,47 @@
1
+ "use strict";var Tina4=(()=>{var H=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var he=Object.prototype.hasOwnProperty;var me=(e,n)=>{for(var t in n)H(e,t,{get:n[t],enumerable:!0})},ye=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of ge(n))!he.call(e,r)&&r!==t&&H(e,r,{get:()=>n[r],enumerable:!(o=pe(n,r))||o.enumerable});return e};var ve=e=>ye(H({},"__esModule",{value:!0}),e);var Ae={};me(Ae,{Tina4Element:()=>I,api:()=>ie,batch:()=>q,computed:()=>z,effect:()=>m,html:()=>X,isSignal:()=>w,navigate:()=>L,pwa:()=>le,route:()=>oe,router:()=>re,signal:()=>h,ws:()=>ue});var C=null,_=null,M=null;function b(e){M=e}function J(){return M}var B=null,V=null;var N=0,P=new Set;function h(e,n){let t=e,o=new Set,r={_t4:!0,get value(){if(C&&(o.add(C),_)){let a=C;_.push(()=>o.delete(a))}return t},set value(a){if(Object.is(a,t))return;let i=t;if(t=a,r._debugInfo&&r._debugInfo.updateCount++,V&&V(r,i,a),N>0)for(let s of o)P.add(s);else{let s;for(let c of[...o])try{c()}catch(l){s===void 0&&(s=l)}if(s!==void 0)throw s}},_subscribe(a){return o.add(a),()=>{o.delete(a)}},peek(){return t}};return B&&(r._debugInfo={label:n,createdAt:Date.now(),updateCount:0,subs:o},B(r,n)),r}function z(e){let n=h(void 0);return m(()=>{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 m(e){let n=!1,t=[],o=()=>{if(n)return;for(let s of t)s();t=[];let a=C,i=_;C=o,_=t;try{e()}finally{C=a,_=i}};o();let r=()=>{n=!0;for(let a of t)a();t=[]};return M&&M.push(r),r}function q(e){N++;try{e()}finally{if(N--,N===0){let n=[...P];P.clear();let t;for(let o of n)try{o()}catch(r){t===void 0&&(t=r)}if(t!==void 0)throw t}}}function w(e){return e!==null&&typeof e=="object"&&e._t4===!0}var Q=new WeakMap,D="t4:";function X(e,...n){let t=Q.get(e);if(!t){t=document.createElement("template");let i="";for(let s=0;s<e.length;s++)i+=e[s],s<n.length&&(Te(i)?i+=`__t4_${s}__`:i+=`<!--${D}${s}-->`);t.innerHTML=i,Q.set(e,t)}let o=t.content.cloneNode(!0),r=be(o);for(let{marker:i,index:s}of r)ke(i,n[s]);let a=we(o);for(let i of a)Ce(i,n);return o}function be(e){let n=[];return W(e,t=>{if(t.nodeType===8){let o=t.data;if(o&&o.startsWith(D)){let r=parseInt(o.slice(D.length),10);n.push({marker:t,index:r})}}}),n}function we(e){let n=[];return W(e,t=>{t.nodeType===1&&n.push(t)}),n}function W(e,n){let t=e.childNodes;for(let o=0;o<t.length;o++){let r=t[o];n(r),W(r,n)}}function ke(e,n){let t=e.parentNode;if(t)if(w(n)){let o=document.createTextNode("");t.replaceChild(o,e),m(()=>{o.data=String(n.value??"")})}else if(typeof n=="function"){let o=document.createComment("");t.replaceChild(o,e);let r=[],a=[];m(()=>{for(let d of a)d();a=[];let i=[],s=J();b(i);let c=n();b(s),a=i;for(let d of r)d.parentNode?.removeChild(d);r=[];let l=F(c),f=o.parentNode;if(f)for(let d of l)f.insertBefore(d,o),r.push(d)})}else if(Y(n))t.replaceChild(n,e);else if(n instanceof Node)t.replaceChild(n,e);else if(Array.isArray(n)){let o=document.createDocumentFragment();for(let r of n){let a=F(r);for(let i of a)o.appendChild(i)}t.replaceChild(o,e)}else{let o=document.createTextNode(String(n??""));t.replaceChild(o,e)}}function Ce(e,n){let t=[];for(let o of Array.from(e.attributes)){let r=o.name,a=o.value;if(r.startsWith("@")){let s=r.slice(1),c=a.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];typeof l=="function"&&e.addEventListener(s,f=>q(()=>l(f)))}t.push(r);continue}if(r.startsWith("?")){let s=r.slice(1),c=a.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];if(w(l)){let f=l;m(()=>{f.value?e.setAttribute(s,""):e.removeAttribute(s)})}else typeof l=="function"?m(()=>{l()?e.setAttribute(s,""):e.removeAttribute(s)}):l&&e.setAttribute(s,"")}t.push(r);continue}if(r.startsWith(".")){let s=r.slice(1),c=a.match(/__t4_(\d+)__/);if(c){let l=n[parseInt(c[1],10)];w(l)?m(()=>{e[s]=l.value}):e[s]=l}t.push(r);continue}let i=a.match(/__t4_(\d+)__/);if(i){let s=n[parseInt(i[1],10)];if(w(s)){let c=s;m(()=>{e.setAttribute(r,String(c.value??""))})}else typeof s=="function"?m(()=>{e.setAttribute(r,String(s()??""))}):e.setAttribute(r,String(s??""))}}for(let o of t)e.removeAttribute(o)}function F(e){if(e==null||e===!1)return[];if(Y(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(...F(t));return n}return[document.createTextNode(String(e))]}function Y(e){return e!=null&&typeof e=="object"&&e.nodeType===11}function Te(e){let n=!1,t=!1,o=!1;for(let r=0;r<e.length;r++){let a=e[r];a==="<"&&!n&&!t&&(o=!0),a===">"&&!n&&!t&&(o=!1),o&&(a==='"'&&!n&&(t=!t),a==="'"&&!t&&(n=!n))}return o}var Z=null,ee=null;var I=class extends HTMLElement{constructor(){super();this._props={};this._rendered=!1;let t=this.constructor;this._root=t.shadow?this.attachShadow({mode:"open"}):this;for(let[o,r]of Object.entries(t.props))this._props[o]=h(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 t=this.constructor;if(t.styles&&t.shadow&&this._root instanceof ShadowRoot){let r=document.createElement("style");r.textContent=t.styles,this._root.appendChild(r)}let o=this.render();o&&this._root.appendChild(o),this.onMount(),Z&&Z(this)}disconnectedCallback(){this.onUnmount(),ee&&ee(this)}attributeChangedCallback(t,o,r){let i=this.constructor.props[t];i&&this._props[t]&&(this._props[t].value=this._coerce(r,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,o){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,...o}))}onMount(){}onUnmount(){}_coerce(t,o){return o===Boolean?t!==null:o===Number?t!==null?Number(t):0:t??""}};var U=[],T=null,S="history",_e=!1,E=[],O=[],te=0;function oe(e,n){let t=[],o;e==="*"?o=".*":o=e.replace(/\{(\w+)\}/g,(a,i)=>(t.push(i),"([^/]+)"));let r=new RegExp(`^${o}$`);typeof n=="function"?U.push({pattern:e,regex:r,paramNames:t,handler:n}):U.push({pattern:e,regex:r,paramNames:t,handler:n.handler,guard:n.guard})}function L(e,n){if(S==="hash")if(n?.replace){let t=new URL(location.href);t.hash="#"+e,history.replaceState(null,"",t.toString()),R()}else location.hash="#"+e;else n?.replace?history.replaceState(null,"",e):history.pushState(null,"",e),R()}function R(){if(!T)return;let e=performance.now(),n=++te,t=S==="hash"?location.hash.slice(1)||"/":location.pathname;for(let o of U){let r=t.match(o.regex);if(!r)continue;let a={};if(o.paramNames.forEach((c,l)=>{a[c]=decodeURIComponent(r[l+1])}),o.guard){let c=o.guard();if(c===!1)return;if(typeof c=="string"){L(c,{replace:!0});return}}for(let c of O)c();O=[],T.innerHTML="";let i=[];b(i);let s=o.handler(a);if(s instanceof Promise)s.then(c=>{if(b(null),n!==te){for(let f of i)f();return}ne(T,c),O=i;let l=performance.now()-e;for(let f of E)f({path:t,params:a,pattern:o.pattern,durationMs:l})});else{b(null),ne(T,s),O=i;let c=performance.now()-e;for(let l of E)l({path:t,params:a,pattern:o.pattern,durationMs:c})}return}}function ne(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 re={start(e){if(T=document.querySelector(e.target),!T)throw new Error(`[tina4] Router target '${e.target}' not found in DOM`);S=e.mode??"history",_e=!0,window.addEventListener("popstate",R),S==="hash"&&window.addEventListener("hashchange",R),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 o=S==="hash"?t.getAttribute("href"):t.pathname;L(o)}),R()},on(e,n){return E.push(n),()=>{let t=E.indexOf(n);t>=0&&E.splice(t,1)}}};var y={baseUrl:"",auth:!1,tokenKey:"tina4_token",headers:{}},j=[],K=[],Se=0;function se(){try{return localStorage.getItem(y.tokenKey)}catch{return null}}function Ee(e){try{localStorage.setItem(y.tokenKey,e)}catch{}}async function x(e,n,t,o){let r={method:e,headers:{"Content-Type":"application/json",...y.headers}};if(y.auth){let d=se();d&&(r.headers.Authorization=`Bearer ${d}`)}if(t!==void 0&&e!=="GET"){let d=typeof t=="object"&&t!==null?{...t}:t;if(y.auth&&typeof d=="object"&&d!==null){let p=se();p&&(d.formToken=p)}r.body=JSON.stringify(d)}if(o?.headers&&Object.assign(r.headers,o.headers),o?.params){let d=Object.entries(o.params).map(([p,v])=>`${encodeURIComponent(p)}=${encodeURIComponent(String(v))}`).join("&");n+=(n.includes("?")?"&":"?")+d}let a=y.baseUrl+n;r._url=a,r._requestId=++Se;for(let d of j){let p=d(r);p&&(r=p)}let i=await fetch(a,r),s=i.headers.get("FreshToken");s&&Ee(s);let c=i.headers.get("Content-Type")??"",l;c.includes("json")?l=await i.json():l=await i.text();let f={status:i.status,data:l,ok:i.ok,headers:i.headers,_requestId:r._requestId};for(let d of K){let p=d(f);p&&(f=p)}if(!i.ok)throw f;return f.data}var ie={configure(e){Object.assign(y,e)},get(e,n){return x("GET",e,void 0,n)},post(e,n,t){return x("POST",e,n,t)},put(e,n,t){return x("PUT",e,n,t)},patch(e,n,t){return x("PATCH",e,n,t)},delete(e,n){return x("DELETE",e,void 0,n)},intercept(e,n){e==="request"?j.push(n):K.push(n)},_reset(){y.baseUrl="",y.auth=!1,y.tokenKey="tina4_token",y.headers={},j.length=0,K.length=0}};function ae(e){let n=e.cacheStrategy??"network-first",t=JSON.stringify(e.precache??[]),o=e.offlineRoute?`'${e.offlineRoute}'`:"null";return`
2
+ const CACHE = 'tina4-v1';
3
+ const PRECACHE = ${t};
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 ce(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 le={register(e){let n=ce(e),t=new Blob([JSON.stringify(n)],{type:"application/json"}),o=document.createElement("link");o.rel="manifest",o.href=URL.createObjectURL(t),document.head.appendChild(o);let r=document.querySelector('meta[name="theme-color"]');if(r||(r=document.createElement("meta"),r.name="theme-color",document.head.appendChild(r)),r.content=e.themeColor??"#000000","serviceWorker"in navigator){let a=ae(e),i=new Blob([a],{type:"text/javascript"}),s=URL.createObjectURL(i);navigator.serviceWorker.register(s).catch(c=>{console.warn("[tina4] Service worker registration failed:",c)})}},generateServiceWorker(e){return ae(e)},generateManifest(e){return ce(e)}};var Re={reconnect:!0,reconnectDelay:1e3,reconnectMaxDelay:3e4,reconnectAttempts:1/0,protocols:[]};function xe(e,n={}){let t={...Re,...n},o=h("connecting"),r=h(!1),a=h(null),i=h(null),s=h(0),c={message:[],open:[],close:[],error:[]},l=null,f=!1,d=t.reconnectDelay,p=null,v=0;function de(u){if(typeof u!="string")return u;try{return JSON.parse(u)}catch{return u}}function $(){o.value=v>0?"reconnecting":"connecting";try{l=new WebSocket(e,t.protocols)}catch{o.value="closed",r.value=!1;return}l.onopen=()=>{o.value="open",r.value=!0,i.value=null,v=0,d=t.reconnectDelay,s.value=0;for(let u of c.open)u()},l.onmessage=u=>{let g=de(u.data);a.value=g;for(let k of c.message)k(g)},l.onclose=u=>{o.value="closed",r.value=!1;for(let g of c.close)g(u.code,u.reason);!f&&t.reconnect&&v<t.reconnectAttempts&&fe()},l.onerror=u=>{i.value=u;for(let g of c.error)g(u)}}function fe(){v++,s.value=v,o.value="reconnecting",p=setTimeout(()=>{p=null,$()},d),d=Math.min(d*2,t.reconnectMaxDelay)}let G={status:o,connected:r,lastMessage:a,error:i,reconnectCount:s,send(u){if(!l||l.readyState!==WebSocket.OPEN)throw new Error("[tina4] WebSocket is not connected");let g=typeof u=="string"?u:JSON.stringify(u);l.send(g)},on(u,g){return c[u].push(g),()=>{let k=c[u],A=k.indexOf(g);A>=0&&k.splice(A,1)}},pipe(u,g){let k=A=>{u.value=g(A,u.value)};return G.on("message",k)},close(u,g){f=!0,p&&(clearTimeout(p),p=null),l&&l.close(u??1e3,g??""),o.value="closed",r.value=!1}};return $(),G}var ue={connect:xe};return ve(Ae);})();
File without changes
File without changes