tina4-python 3.10.38__tar.gz → 3.10.39__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 (143) hide show
  1. {tina4_python-3.10.38 → tina4_python-3.10.39}/PKG-INFO +1 -1
  2. {tina4_python-3.10.38 → tina4_python-3.10.39}/pyproject.toml +1 -1
  3. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/auth/__init__.py +47 -6
  4. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/cli/__init__.py +1 -1
  5. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/router.py +7 -2
  6. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/server.py +3 -3
  7. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/dev_admin/__init__.py +2 -2
  8. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/orm/model.py +12 -2
  9. {tina4_python-3.10.38 → tina4_python-3.10.39}/.gitignore +0 -0
  10. {tina4_python-3.10.38 → tina4_python-3.10.39}/README.md +0 -0
  11. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/CLAUDE.md +0 -0
  12. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/HtmlElement.py +0 -0
  13. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/Testing.py +0 -0
  14. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/__init__.py +0 -0
  15. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/ai/__init__.py +0 -0
  16. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/api/__init__.py +0 -0
  17. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/cache/__init__.py +0 -0
  18. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/container/__init__.py +0 -0
  19. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/__init__.py +0 -0
  20. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/cache.py +0 -0
  21. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/constants.py +0 -0
  22. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/events.py +0 -0
  23. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/middleware.py +0 -0
  24. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/request.py +0 -0
  25. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/core/response.py +0 -0
  26. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/crud/__init__.py +0 -0
  27. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/__init__.py +0 -0
  28. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/adapter.py +0 -0
  29. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/connection.py +0 -0
  30. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/firebird.py +0 -0
  31. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/mssql.py +0 -0
  32. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/mysql.py +0 -0
  33. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/odbc.py +0 -0
  34. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/postgres.py +0 -0
  35. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/database/sqlite.py +0 -0
  36. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/debug/__init__.py +0 -0
  37. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/debug/error_overlay.py +0 -0
  38. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/dev_admin/metrics.py +0 -0
  39. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/dev_reload.py +0 -0
  40. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/dotenv/__init__.py +0 -0
  41. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/frond/FROND.md +0 -0
  42. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/frond/__init__.py +0 -0
  43. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/frond/engine.py +0 -0
  44. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/auth/meta.json +0 -0
  45. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  46. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/database/meta.json +0 -0
  47. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  48. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/error-overlay/meta.json +0 -0
  49. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  50. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/orm/meta.json +0 -0
  51. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  52. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  53. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/queue/meta.json +0 -0
  54. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  55. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/rest-api/meta.json +0 -0
  56. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  57. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/templates/meta.json +0 -0
  58. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  59. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  60. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/graphql/__init__.py +0 -0
  61. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/i18n/__init__.py +0 -0
  62. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/mcp/__init__.py +0 -0
  63. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/mcp/protocol.py +0 -0
  64. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/mcp/tools.py +0 -0
  65. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/messenger/__init__.py +0 -0
  66. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/migration/__init__.py +0 -0
  67. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/migration/runner.py +0 -0
  68. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/orm/__init__.py +0 -0
  69. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/orm/fields.py +0 -0
  70. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/css/tina4.css +0 -0
  71. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/css/tina4.min.css +0 -0
  72. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/favicon.ico +0 -0
  73. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/images/logo.svg +0 -0
  74. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  75. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/js/frond.min.js +0 -0
  76. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  77. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/js/tina4.min.js +0 -0
  78. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/js/tina4js.min.js +0 -0
  79. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/swagger/index.html +0 -0
  80. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  81. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/query_builder/__init__.py +0 -0
  82. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/queue/__init__.py +0 -0
  83. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/queue_backends/__init__.py +0 -0
  84. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/queue_backends/kafka_backend.py +0 -0
  85. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/queue_backends/mongo_backend.py +0 -0
  86. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  87. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/__init__.py +0 -0
  88. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  89. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_badges.scss +0 -0
  90. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  91. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_cards.scss +0 -0
  92. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_forms.scss +0 -0
  93. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_grid.scss +0 -0
  94. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_modals.scss +0 -0
  95. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_nav.scss +0 -0
  96. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_reset.scss +0 -0
  97. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_tables.scss +0 -0
  98. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_typography.scss +0 -0
  99. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  100. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/_variables.scss +0 -0
  101. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/base.scss +0 -0
  102. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/colors.scss +0 -0
  103. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/scss/tina4css/tina4.scss +0 -0
  104. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/seeder/__init__.py +0 -0
  105. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/service/__init__.py +0 -0
  106. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/session/__init__.py +0 -0
  107. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/session_handlers/__init__.py +0 -0
  108. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  109. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/session_handlers/redis_handler.py +0 -0
  110. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/session_handlers/valkey_handler.py +0 -0
  111. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/swagger/__init__.py +0 -0
  112. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/components/crud.twig +0 -0
  113. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  114. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  115. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/docker/python/Dockerfile +0 -0
  116. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  117. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/302.twig +0 -0
  118. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/401.twig +0 -0
  119. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/403.twig +0 -0
  120. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/404.twig +0 -0
  121. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/500.twig +0 -0
  122. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/502.twig +0 -0
  123. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/503.twig +0 -0
  124. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/errors/base.twig +0 -0
  125. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/frontend/README.md +0 -0
  126. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/templates/readme.md +0 -0
  127. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/test_client/__init__.py +0 -0
  128. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  129. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  130. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  131. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  132. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  133. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  134. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  135. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  136. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  137. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  138. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  139. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  140. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/validator/__init__.py +0 -0
  141. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/websocket/__init__.py +0 -0
  142. {tina4_python-3.10.38 → tina4_python-3.10.39}/tina4_python/websocket/backplane.py +0 -0
  143. {tina4_python-3.10.38 → tina4_python-3.10.39}/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.10.38
3
+ Version: 3.10.39
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.10.38"
3
+ version = "3.10.39"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -14,7 +14,7 @@ No PyJWT, no cryptography package.
14
14
  payload = auth.valid_token(token)
15
15
 
16
16
  hashed = Auth.hash_password("secret123")
17
- Auth.check_password(hashed, "secret123") # True
17
+ Auth.check_password("secret123", hashed) # True
18
18
  """
19
19
  import os
20
20
  import hmac
@@ -152,6 +152,25 @@ class Auth:
152
152
  auth = cls(secret=secret, expires_in=expires_in)
153
153
  return auth.refresh_token(token)
154
154
 
155
+ @classmethod
156
+ def authenticate_request_static(cls, headers: dict) -> dict | None:
157
+ """Extract and validate auth from request headers without instantiating Auth.
158
+
159
+ Reads SECRET from env. Checks: Bearer JWT, Bearer API key, Basic auth.
160
+ Returns payload dict on success, None on failure.
161
+ """
162
+ secret = os.environ.get("SECRET", "tina4-default-secret")
163
+ auth = cls(secret=secret)
164
+ return auth.authenticate_request(headers)
165
+
166
+ @staticmethod
167
+ def validate_api_key_static(provided: str, expected: str = None) -> bool:
168
+ """Validate an API key without instantiating Auth.
169
+
170
+ Alias for validate_api_key (already a staticmethod).
171
+ """
172
+ return Auth.validate_api_key(provided, expected)
173
+
155
174
  # ── Password Hashing ──────────────────────────────────────────
156
175
 
157
176
  @staticmethod
@@ -164,7 +183,7 @@ class Auth:
164
183
  return f"pbkdf2_sha256${iterations}${salt}${dk.hex()}"
165
184
 
166
185
  @staticmethod
167
- def check_password(hashed: str, password: str) -> bool:
186
+ def check_password(password: str, hashed: str) -> bool:
168
187
  """Verify a password against its PBKDF2 hash."""
169
188
  try:
170
189
  parts = hashed.split("$")
@@ -183,9 +202,18 @@ class Auth:
183
202
  # ── API Key Auth ──────────────────────────────────────────────
184
203
 
185
204
  @staticmethod
186
- def validate_api_key(provided: str) -> bool:
187
- """Check a Bearer token against the TINA4_API_KEY env var (falls back to API_KEY)."""
188
- expected = os.environ.get("TINA4_API_KEY", os.environ.get("API_KEY", ""))
205
+ def validate_api_key(provided: str, expected: str = None) -> bool:
206
+ """Check an API key against an expected value.
207
+
208
+ Args:
209
+ provided: The API key provided in the request.
210
+ expected: The expected API key. If None, reads from
211
+ TINA4_API_KEY env var (falls back to API_KEY).
212
+
213
+ Returns: True if the provided key matches.
214
+ """
215
+ if expected is None:
216
+ expected = os.environ.get("TINA4_API_KEY", os.environ.get("API_KEY", ""))
189
217
  if not expected:
190
218
  return False
191
219
  return hmac.compare_digest(provided, expected)
@@ -255,4 +283,17 @@ def refresh_token(token: str, expires_in: int = 60) -> str | None:
255
283
  return Auth.refresh_token_static(token, expires_in=expires_in)
256
284
 
257
285
 
258
- __all__ = ["Auth", "get_token", "valid_token", "get_payload", "refresh_token"]
286
+ def authenticate_request(headers: dict) -> dict | None:
287
+ """Validate auth from request headers — reads SECRET from env."""
288
+ return Auth.authenticate_request_static(headers)
289
+
290
+
291
+ def validate_api_key(provided: str, expected: str = None) -> bool:
292
+ """Validate an API key. Shortcut for Auth.validate_api_key()."""
293
+ return Auth.validate_api_key(provided, expected)
294
+
295
+
296
+ __all__ = [
297
+ "Auth", "get_token", "valid_token", "get_payload", "refresh_token",
298
+ "authenticate_request", "validate_api_key",
299
+ ]
@@ -363,7 +363,7 @@ def _routes(args):
363
363
  importlib.import_module("app")
364
364
 
365
365
  from tina4_python.core.router import Router
366
- routes = Router.all()
366
+ routes = Router.get_routes()
367
367
  if not routes:
368
368
  print("No routes registered.")
369
369
  return
@@ -231,8 +231,13 @@ class Router:
231
231
  return None, {}
232
232
 
233
233
  @staticmethod
234
- def all() -> list[dict]:
235
- """Return all registered routes (for CLI listing and Swagger)."""
234
+ def get_routes() -> list[dict]:
235
+ """Return all registered routes."""
236
+ return _routes
237
+
238
+ @staticmethod
239
+ def list_routes() -> list[dict]:
240
+ """Return all registered routes (debug-friendly)."""
236
241
  return _routes
237
242
 
238
243
  @staticmethod
@@ -757,7 +757,7 @@ async def app(scope: dict, receive, send):
757
757
  # Serve OpenAPI spec JSON from all registered routes
758
758
  from tina4_python.swagger import Swagger as _SwaggerGen
759
759
  _swagger = _SwaggerGen()
760
- _spec = _swagger.generate(Router.all())
760
+ _spec = _swagger.generate(Router.get_routes())
761
761
  response.json(_spec)
762
762
  _cors.apply(request, response)
763
763
  headers = response.build_headers("")
@@ -929,7 +929,7 @@ async def app(scope: dict, receive, send):
929
929
  matched_pattern = route["path"] if route else "-"
930
930
  toolbar = render_dev_toolbar(
931
931
  request.method, request.path, matched_pattern,
932
- request_id, len(Router.all()),
932
+ request_id, len(Router.get_routes()),
933
933
  ).encode()
934
934
  content = response.content
935
935
  # Inject before </body> if present, else append
@@ -1216,7 +1216,7 @@ def run(host: str | None = None, port: int | None = None):
1216
1216
 
1217
1217
  # Auto-discover routes
1218
1218
  _auto_discover("src")
1219
- route_count = len(Router.all())
1219
+ route_count = len(Router.get_routes())
1220
1220
  Log.info(f"Discovered {route_count} routes")
1221
1221
 
1222
1222
  # Resolve host/port (CLI arg > ENV > default)
@@ -309,7 +309,7 @@ async def _api_routes(request, response):
309
309
  try:
310
310
  from tina4_python.core.router import Router
311
311
  internal_prefixes = ("/__dev", "/health", "/swagger")
312
- routes = Router.all()
312
+ routes = Router.get_routes()
313
313
  result = []
314
314
  for r in routes:
315
315
  path = r.get("path", "")
@@ -926,7 +926,7 @@ async def _api_tool(request, response):
926
926
  "test": [sys.executable, "-m", "pytest", "tests/", "-q", "--tb=short"],
927
927
  "routes": [sys.executable, "-c",
928
928
  "from tina4_python.core.router import Router; "
929
- "[print(f\"{r['method']:7} {r['path']}\") for r in Router.all()]"],
929
+ "[print(f\"{r['method']:7} {r['path']}\") for r in Router.get_routes()]"],
930
930
  "migrate": [sys.executable, "-c",
931
931
  "from tina4_python.cli import _migrate; _migrate([])"],
932
932
  "seed": [sys.executable, "-c",
@@ -318,7 +318,7 @@ class ORM(metaclass=ORMMeta):
318
318
  # ── Finders ─────────────────────────────────────────────────
319
319
 
320
320
  @classmethod
321
- def find(cls, pk_value, include: list[str] = None):
321
+ def find_by_id(cls, pk_value, include: list[str] = None):
322
322
  """Find a single record by primary key. Returns instance or None.
323
323
 
324
324
  Args:
@@ -342,10 +342,20 @@ class ORM(metaclass=ORMMeta):
342
342
  cls._eager_load([instance], include)
343
343
  return instance
344
344
 
345
+ @classmethod
346
+ def find(cls, pk_value, include: list[str] = None):
347
+ """Alias for find_by_id()."""
348
+ return cls.find_by_id(pk_value, include)
349
+
350
+ @classmethod
351
+ def load(cls, pk_value, include: list[str] = None):
352
+ """Alias for find_by_id()."""
353
+ return cls.find_by_id(pk_value, include)
354
+
345
355
  @classmethod
346
356
  def find_or_fail(cls, pk_value):
347
357
  """Find by primary key or raise ValueError."""
348
- result = cls.find(pk_value)
358
+ result = cls.find_by_id(pk_value)
349
359
  if result is None:
350
360
  raise ValueError(f"{cls.__name__} with {cls._get_pk()}={pk_value} not found")
351
361
  return result
File without changes