tempest-fastapi-sdk 0.81.2__tar.gz → 0.82.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 (462) hide show
  1. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/CHANGELOG.md +16 -0
  2. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/PKG-INFO +2 -2
  3. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/README.md +1 -1
  4. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/sse.en.md +64 -45
  5. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/sse.md +64 -45
  6. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/reference.md +1 -0
  7. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/pyproject.toml +1 -1
  8. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/__init__.py +8 -2
  9. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sse/__init__.py +2 -0
  10. tempest_fastapi_sdk-0.82.0/tempest_fastapi_sdk/sse/broker.py +208 -0
  11. tempest_fastapi_sdk-0.82.0/tests/sse/test_broker.py +80 -0
  12. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/uv.lock +1 -1
  13. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/.github/workflows/ci.yml +0 -0
  14. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/.github/workflows/docs.yml +0 -0
  15. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/.github/workflows/release-pypi.yml +0 -0
  16. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/.gitignore +0 -0
  17. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/.python-version +0 -0
  18. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/CLAUDE.md +0 -0
  19. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/Makefile +0 -0
  20. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/architecture.en.md +0 -0
  21. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/architecture.md +0 -0
  22. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/changelog.en.md +0 -0
  23. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/changelog.md +0 -0
  24. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/contributing.en.md +0 -0
  25. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/contributing.md +0 -0
  26. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/index.en.md +0 -0
  27. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/index.md +0 -0
  28. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/installation.en.md +0 -0
  29. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/installation.md +0 -0
  30. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/index.en.md +0 -0
  31. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/index.md +0 -0
  32. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/api.en.md +0 -0
  33. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/api.md +0 -0
  34. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/business-rules.en.md +0 -0
  35. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/business-rules.md +0 -0
  36. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/domain.en.md +0 -0
  37. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/domain.md +0 -0
  38. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/flows.en.md +0 -0
  39. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/flows.md +0 -0
  40. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/index.en.md +0 -0
  41. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/learning/marketplace/index.md +0 -0
  42. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/migration.en.md +0 -0
  43. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/migration.md +0 -0
  44. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/admin.en.md +0 -0
  45. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/admin.md +0 -0
  46. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/audit-trail.en.md +0 -0
  47. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/audit-trail.md +0 -0
  48. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/auth-flow.en.md +0 -0
  49. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/auth-flow.md +0 -0
  50. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/br-helpers.en.md +0 -0
  51. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/br-helpers.md +0 -0
  52. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/cache.en.md +0 -0
  53. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/cache.md +0 -0
  54. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/cli.en.md +0 -0
  55. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/cli.md +0 -0
  56. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/database.en.md +0 -0
  57. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/database.md +0 -0
  58. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/deploy-safety.en.md +0 -0
  59. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/deploy-safety.md +0 -0
  60. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/downloads.en.md +0 -0
  61. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/downloads.md +0 -0
  62. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/email.en.md +0 -0
  63. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/email.md +0 -0
  64. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/feature-flags.en.md +0 -0
  65. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/feature-flags.md +0 -0
  66. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/fields.en.md +0 -0
  67. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/fields.md +0 -0
  68. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/http-client.en.md +0 -0
  69. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/http-client.md +0 -0
  70. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/http.en.md +0 -0
  71. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/http.md +0 -0
  72. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/idempotency.en.md +0 -0
  73. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/idempotency.md +0 -0
  74. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/index.en.md +0 -0
  75. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/index.md +0 -0
  76. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/logging.en.md +0 -0
  77. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/logging.md +0 -0
  78. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/metrics.en.md +0 -0
  79. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/metrics.md +0 -0
  80. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/mfa.en.md +0 -0
  81. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/mfa.md +0 -0
  82. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/multi-tenant.en.md +0 -0
  83. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/multi-tenant.md +0 -0
  84. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/observability.en.md +0 -0
  85. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/observability.md +0 -0
  86. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/offline-sync.en.md +0 -0
  87. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/offline-sync.md +0 -0
  88. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/outbox.en.md +0 -0
  89. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/outbox.md +0 -0
  90. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/queue-tasks.en.md +0 -0
  91. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/queue-tasks.md +0 -0
  92. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/realtime.en.md +0 -0
  93. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/realtime.md +0 -0
  94. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/refresh-tokens.en.md +0 -0
  95. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/refresh-tokens.md +0 -0
  96. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/security.en.md +0 -0
  97. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/security.md +0 -0
  98. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/sessions.en.md +0 -0
  99. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/sessions.md +0 -0
  100. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/storage.en.md +0 -0
  101. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/storage.md +0 -0
  102. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/stored-files.en.md +0 -0
  103. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/stored-files.md +0 -0
  104. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/testing.en.md +0 -0
  105. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/testing.md +0 -0
  106. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/typing.en.md +0 -0
  107. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/typing.md +0 -0
  108. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/uploads.en.md +0 -0
  109. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/uploads.md +0 -0
  110. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/utilities.en.md +0 -0
  111. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/utilities.md +0 -0
  112. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/webpush.en.md +0 -0
  113. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/webpush.md +0 -0
  114. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/websocket.en.md +0 -0
  115. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/recipes/websocket.md +0 -0
  116. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/reference.en.md +0 -0
  117. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/roadmap.en.md +0 -0
  118. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/roadmap.md +0 -0
  119. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/tutorial.en.md +0 -0
  120. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/docs/tutorial.md +0 -0
  121. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/mkdocs.yml +0 -0
  122. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/mkdocs_hooks/llmstxt.py +0 -0
  123. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/scripts/extract_recipe.py +0 -0
  124. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
  125. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
  126. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/config.py +0 -0
  127. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/discovery.py +0 -0
  128. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
  129. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/router.py +0 -0
  130. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/session.py +0 -0
  131. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/site.py +0 -0
  132. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/static/admin.css +0 -0
  133. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
  134. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
  135. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
  136. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
  137. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
  138. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
  139. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
  140. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
  141. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/admin/theme.py +0 -0
  142. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
  143. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
  144. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
  145. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
  146. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
  147. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
  148. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
  149. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
  150. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
  151. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
  152. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
  153. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
  154. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
  155. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
  156. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
  157. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
  158. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
  159. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
  160. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
  161. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/server.py +0 -0
  162. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/static.py +0 -0
  163. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
  164. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
  165. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
  166. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/guards.py +0 -0
  167. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/locale.py +0 -0
  168. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
  169. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/router.py +0 -0
  170. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
  171. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/service.py +0 -0
  172. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/activation.html +0 -0
  173. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_error.html +0 -0
  174. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_success.html +0 -0
  175. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset.html +0 -0
  176. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_error.html +0 -0
  177. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_form.html +0 -0
  178. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_success.html +0 -0
  179. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation.html +0 -0
  180. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_error.html +0 -0
  181. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_success.html +0 -0
  182. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset.html +0 -0
  183. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_error.html +0 -0
  184. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_form.html +0 -0
  185. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_success.html +0 -0
  186. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
  187. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
  188. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cache/invalidation.py +0 -0
  189. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
  190. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
  191. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/Dockerfile.tmpl +0 -0
  192. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
  193. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/dockerignore.tmpl +0 -0
  194. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
  195. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
  196. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
  197. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
  198. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
  199. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
  200. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
  201. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
  202. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
  203. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
  204. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
  205. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
  206. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
  207. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
  208. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
  209. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
  210. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
  211. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
  212. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
  213. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
  214. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
  215. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
  216. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
  217. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
  218. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
  219. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/config.py +0 -0
  220. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/db.py +0 -0
  221. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
  222. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
  223. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
  224. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/main.py +0 -0
  225. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/new.py +0 -0
  226. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/secrets.py +0 -0
  227. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
  228. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/cli/user.py +0 -0
  229. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
  230. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
  231. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
  232. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/core/context.py +0 -0
  233. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/core/enums.py +0 -0
  234. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/core/logging.py +0 -0
  235. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/core/typed.py +0 -0
  236. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
  237. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
  238. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
  239. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
  240. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/audit.py +0 -0
  241. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/backup.py +0 -0
  242. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/connection.py +0 -0
  243. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
  244. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
  245. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/model.py +0 -0
  246. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
  247. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/repository.py +0 -0
  248. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
  249. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
  250. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
  251. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
  252. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/user_refresh_token_model.py +0 -0
  253. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
  254. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/db/webpush_subscription_model.py +0 -0
  255. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
  256. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
  257. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
  258. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
  259. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/i18n.py +0 -0
  260. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
  261. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
  262. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
  263. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
  264. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
  265. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
  266. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/flags/__init__.py +0 -0
  267. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/flags/backends.py +0 -0
  268. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/flags/dependencies.py +0 -0
  269. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/flags/service.py +0 -0
  270. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/py.typed +0 -0
  271. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
  272. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
  273. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
  274. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
  275. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
  276. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
  277. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
  278. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
  279. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
  280. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/services/base.py +0 -0
  281. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/services/file_mixin.py +0 -0
  282. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
  283. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
  284. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
  285. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
  286. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
  287. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
  288. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
  289. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
  290. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/settings/base.py +0 -0
  291. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
  292. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
  293. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
  294. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
  295. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
  296. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
  297. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
  298. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
  299. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/testing/database.py +0 -0
  300. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
  301. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
  302. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/data/br_locations.json +0 -0
  303. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
  304. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
  305. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/download.py +0 -0
  306. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/email.py +0 -0
  307. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/fields.py +0 -0
  308. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
  309. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
  310. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/locations.py +0 -0
  311. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/log.py +0 -0
  312. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
  313. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
  314. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/password.py +0 -0
  315. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
  316. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
  317. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
  318. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
  319. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
  320. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
  321. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
  322. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/webpush/router.py +0 -0
  323. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
  324. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/webpush/service.py +0 -0
  325. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
  326. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
  327. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
  328. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
  329. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/__init__.py +0 -0
  330. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/__init__.py +0 -0
  331. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_auth.py +0 -0
  332. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_discovery.py +0 -0
  333. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_forms.py +0 -0
  334. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_logs_nav.py +0 -0
  335. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_mfa.py +0 -0
  336. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_router.py +0 -0
  337. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_site.py +0 -0
  338. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_theme.py +0 -0
  339. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/admin/test_user_model.py +0 -0
  340. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/__init__.py +0 -0
  341. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_body_size.py +0 -0
  342. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_cookies.py +0 -0
  343. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_cors.py +0 -0
  344. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_csrf.py +0 -0
  345. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_dependencies_auth.py +0 -0
  346. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_graceful.py +0 -0
  347. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_handlers.py +0 -0
  348. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_health_router.py +0 -0
  349. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_idempotency.py +0 -0
  350. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_jwt_dependency.py +0 -0
  351. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_logs_router.py +0 -0
  352. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_oauth.py +0 -0
  353. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_prometheus.py +0 -0
  354. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_rate_limit.py +0 -0
  355. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_rate_limit_extras.py +0 -0
  356. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_request_id_middleware.py +0 -0
  357. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_role_dependency.py +0 -0
  358. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_server.py +0 -0
  359. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_static.py +0 -0
  360. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_tool_spec.py +0 -0
  361. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_tracing.py +0 -0
  362. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_webhooks.py +0 -0
  363. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/api/test_webhooks_rsa.py +0 -0
  364. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/__init__.py +0 -0
  365. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_guards.py +0 -0
  366. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_locale.py +0 -0
  367. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_mfa.py +0 -0
  368. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_refresh.py +0 -0
  369. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_refresh_db.py +0 -0
  370. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/auth/test_service.py +0 -0
  371. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cache/__init__.py +0 -0
  372. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cache/test_decorator.py +0 -0
  373. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cache/test_invalidation.py +0 -0
  374. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cache/test_redis_manager.py +0 -0
  375. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/__init__.py +0 -0
  376. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_config.py +0 -0
  377. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_db.py +0 -0
  378. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_db_seed.py +0 -0
  379. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_docker_compose.py +0 -0
  380. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_generate.py +0 -0
  381. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_lint_strictness.py +0 -0
  382. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_main.py +0 -0
  383. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_secrets.py +0 -0
  384. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/cli/test_user.py +0 -0
  385. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/conftest.py +0 -0
  386. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/controllers/__init__.py +0 -0
  387. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/controllers/test_base.py +0 -0
  388. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/core/__init__.py +0 -0
  389. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/core/test_context.py +0 -0
  390. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/core/test_enums.py +0 -0
  391. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/core/test_logging.py +0 -0
  392. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/core/test_typed.py +0 -0
  393. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/__init__.py +0 -0
  394. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_alembic_hooks.py +0 -0
  395. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_audit.py +0 -0
  396. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_backup.py +0 -0
  397. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_bulk_ops.py +0 -0
  398. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_connection.py +0 -0
  399. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_migrations.py +0 -0
  400. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_mixins.py +0 -0
  401. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_model.py +0 -0
  402. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_outbox.py +0 -0
  403. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_repository.py +0 -0
  404. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_safe_upgrade.py +0 -0
  405. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_slow_query.py +0 -0
  406. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/db/test_tenant.py +0 -0
  407. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/exceptions/__init__.py +0 -0
  408. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/exceptions/test_exceptions.py +0 -0
  409. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/exceptions/test_i18n.py +0 -0
  410. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/flags/__init__.py +0 -0
  411. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/flags/test_flags.py +0 -0
  412. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/queue/__init__.py +0 -0
  413. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/queue/test_manager.py +0 -0
  414. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/__init__.py +0 -0
  415. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/test_base.py +0 -0
  416. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/test_cursor_pagination.py +0 -0
  417. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/test_link_headers.py +0 -0
  418. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/test_pagination.py +0 -0
  419. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/schemas/test_response.py +0 -0
  420. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/services/__init__.py +0 -0
  421. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/services/test_base.py +0 -0
  422. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/services/test_file_mixin.py +0 -0
  423. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/sessions/__init__.py +0 -0
  424. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/sessions/test_sessions.py +0 -0
  425. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/settings/__init__.py +0 -0
  426. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/settings/test_base.py +0 -0
  427. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/settings/test_mixins.py +0 -0
  428. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/sse/__init__.py +0 -0
  429. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/sse/test_event_stream.py +0 -0
  430. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/storage/__init__.py +0 -0
  431. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/storage/test_minio_client.py +0 -0
  432. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/tasks/__init__.py +0 -0
  433. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/tasks/test_manager.py +0 -0
  434. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/tasks/test_scheduler.py +0 -0
  435. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/testing/__init__.py +0 -0
  436. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/testing/test_database.py +0 -0
  437. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/__init__.py +0 -0
  438. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_client_ip.py +0 -0
  439. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_datetime.py +0 -0
  440. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_dict.py +0 -0
  441. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_download.py +0 -0
  442. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_email.py +0 -0
  443. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_fields.py +0 -0
  444. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_http_client.py +0 -0
  445. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_jwt.py +0 -0
  446. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_lazy_extras.py +0 -0
  447. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_locations.py +0 -0
  448. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_log.py +0 -0
  449. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_metrics.py +0 -0
  450. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_opaque_token.py +0 -0
  451. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_password.py +0 -0
  452. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_regex.py +0 -0
  453. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_storage_backends.py +0 -0
  454. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_throttle.py +0 -0
  455. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/utils/test_upload.py +0 -0
  456. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/webpush/__init__.py +0 -0
  457. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/webpush/test_dispatcher.py +0 -0
  458. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/webpush/test_router.py +0 -0
  459. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/webpush/test_schemas.py +0 -0
  460. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/webpush/test_service.py +0 -0
  461. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/websockets/__init__.py +0 -0
  462. {tempest_fastapi_sdk-0.81.2 → tempest_fastapi_sdk-0.82.0}/tests/websockets/test_hub_and_router.py +0 -0
@@ -5,6 +5,22 @@ All notable changes to **tempest-fastapi-sdk** are listed below.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.82.0] — 2026-06-28
9
+
10
+ ### Added
11
+
12
+ - **`SSEBroker` — multi-worker SSE fan-out.** The SSE recipe described a
13
+ Redis Pub/Sub bridge for broadcasting across workers but shipped no
14
+ primitive; `SSEBroker` is that primitive. It keeps a per-channel
15
+ registry of local `EventStream`s and fans `publish(channel, ...)` out
16
+ to them. Pass a `[cache]` Redis client and the same broker publishes
17
+ via Redis `PUBLISH` while a background `run()` task `PSUBSCRIBE`-s the
18
+ channel prefix and relays every message to each worker's local
19
+ streams — so `publish` becomes cross-process with no call-site change
20
+ (`register` / `unregister` / `publish` are identical in both modes).
21
+ Exported from `tempest_fastapi_sdk` and `tempest_fastapi_sdk.sse`. The
22
+ SSE recipe now shows the in-memory and Redis-lifespan setups.
23
+
8
24
  ## [0.81.2] — 2026-06-28
9
25
 
10
26
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempest-fastapi-sdk
3
- Version: 0.81.2
3
+ Version: 0.82.0
4
4
  Summary: Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects.
5
5
  Project-URL: Homepage, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
6
6
  Project-URL: Repository, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
@@ -250,7 +250,7 @@ Since `0.7.1` every optional dependency is imported lazily at first instantiatio
250
250
  | `tempest_fastapi_sdk.services` | `BaseService` |
251
251
  | `tempest_fastapi_sdk.core` | `configure_logging`, `JSONFormatter`, `get_request_id`/`set_request_id`/`clear_request_id`, `request_id_ctx`, `BaseStrEnum`, `BaseIntEnum`, `strict_types`/`typed`/`require_annotations` |
252
252
  | `tempest_fastapi_sdk.admin` *(extra: `[admin]`)* | `AdminSite`, `AdminModel`, `make_admin_router`, `AdminAuthBackend`, `UserModelAuthBackend`, `AdminAuthError` |
253
- | `tempest_fastapi_sdk.sse` | `EventStream`, `ServerSentEvent`, `sse_response` |
253
+ | `tempest_fastapi_sdk.sse` | `EventStream`, `SSEBroker` (multi-worker fan-out via Redis), `ServerSentEvent`, `sse_response` |
254
254
  | `tempest_fastapi_sdk.cache` *(extra: `[cache]`)* | `AsyncRedisManager`, `cached` (with `namespace` / `tags`), `CacheInvalidator`, `namespace_registry_key`, `tag_registry_key` |
255
255
  | `tempest_fastapi_sdk.flags` | `FeatureFlags`, `FeatureFlagBackend`, `MemoryFeatureFlagBackend`, `EnvFeatureFlagBackend`, `RedisFeatureFlagBackend`, `CompositeFeatureFlagBackend`, `make_flag_dependency`, `coerce_flag` |
256
256
  | `tempest_fastapi_sdk.webpush` *(extra: `[webpush]`)* | `WebPushDispatcher`, `WebPushSubscriptionService`, `make_web_push_router`, `WebPushError`, `WebPushGoneError`, `WebPushSubscriptionSchema`, `WebPushKeysSchema`, `WebPushPayloadSchema` |
@@ -147,7 +147,7 @@ Since `0.7.1` every optional dependency is imported lazily at first instantiatio
147
147
  | `tempest_fastapi_sdk.services` | `BaseService` |
148
148
  | `tempest_fastapi_sdk.core` | `configure_logging`, `JSONFormatter`, `get_request_id`/`set_request_id`/`clear_request_id`, `request_id_ctx`, `BaseStrEnum`, `BaseIntEnum`, `strict_types`/`typed`/`require_annotations` |
149
149
  | `tempest_fastapi_sdk.admin` *(extra: `[admin]`)* | `AdminSite`, `AdminModel`, `make_admin_router`, `AdminAuthBackend`, `UserModelAuthBackend`, `AdminAuthError` |
150
- | `tempest_fastapi_sdk.sse` | `EventStream`, `ServerSentEvent`, `sse_response` |
150
+ | `tempest_fastapi_sdk.sse` | `EventStream`, `SSEBroker` (multi-worker fan-out via Redis), `ServerSentEvent`, `sse_response` |
151
151
  | `tempest_fastapi_sdk.cache` *(extra: `[cache]`)* | `AsyncRedisManager`, `cached` (with `namespace` / `tags`), `CacheInvalidator`, `namespace_registry_key`, `tag_registry_key` |
152
152
  | `tempest_fastapi_sdk.flags` | `FeatureFlags`, `FeatureFlagBackend`, `MemoryFeatureFlagBackend`, `EnvFeatureFlagBackend`, `RedisFeatureFlagBackend`, `CompositeFeatureFlagBackend`, `make_flag_dependency`, `coerce_flag` |
153
153
  | `tempest_fastapi_sdk.webpush` *(extra: `[webpush]`)* | `WebPushDispatcher`, `WebPushSubscriptionService`, `make_web_push_router`, `WebPushError`, `WebPushGoneError`, `WebPushSubscriptionSchema`, `WebPushKeysSchema`, `WebPushPayloadSchema` |
@@ -93,77 +93,96 @@ stream is idle so load-balancers don't cut the connection. Comments are
93
93
  **invisible** to `EventSource` — they fire no listener, they just keep
94
94
  the socket alive. `None` disables the heartbeat.
95
95
 
96
- ## Broadcast to many clients
96
+ ## Broadcast to many clients (`SSEBroker`)
97
97
 
98
- `EventStream` is one connection. To send the same event to everyone (or
99
- to a user's devices), keep a registry of streams and publish to all — a
100
- simple "hub":
98
+ `EventStream` is **one** connection. To send the same event to every
99
+ client of a channel (e.g. a user's devices, or a topic), the SDK ships
100
+ `SSEBroker` — a per-channel stream registry plus fan-out. The channel is
101
+ any string (a user id, a room slug...).
101
102
 
102
103
  ```python
103
- # src/services/sse_hub.py
104
- from uuid import UUID
105
-
106
- from tempest_fastapi_sdk import EventStream
104
+ # src/api/dependencies/resources.py
105
+ from tempest_fastapi_sdk import SSEBroker
107
106
 
107
+ broker = SSEBroker() # singleton — keep on app.state and inject via Depends
108
+ ```
108
109
 
109
- class SSEHub:
110
- """In-memory registry of open SSE streams, per user."""
110
+ ```python
111
+ # src/api/routers/feed.py
112
+ from uuid import UUID
111
113
 
112
- def __init__(self) -> None:
113
- self._streams: dict[UUID, set[EventStream]] = {}
114
+ from fastapi import APIRouter, Depends
115
+ from starlette.responses import StreamingResponse
114
116
 
115
- def register(self, user_id: UUID) -> EventStream:
116
- """Open a stream for a client and register it."""
117
- stream = EventStream(heartbeat_seconds=15.0)
118
- self._streams.setdefault(user_id, set()).add(stream)
119
- return stream
117
+ from tempest_fastapi_sdk import sse_response
120
118
 
121
- def unregister(self, user_id: UUID, stream: EventStream) -> None:
122
- """Drop a closed stream from the registry."""
123
- streams = self._streams.get(user_id)
124
- if streams:
125
- streams.discard(stream)
126
- if not streams:
127
- del self._streams[user_id]
119
+ router = APIRouter()
128
120
 
129
- async def publish_to_user(self, user_id: UUID, data: object, *, event: str) -> int:
130
- """Publish an event to all of a user's open streams."""
131
- streams = self._streams.get(user_id, set())
132
- for stream in streams:
133
- await stream.publish(data, event=event)
134
- return len(streams)
135
- ```
136
121
 
137
- ```python
138
- # src/api/routers/feed.py
139
122
  @router.get("/feed")
140
123
  async def feed(
141
124
  user_id: UUID = Depends(get_current_user_id),
142
- hub: SSEHub = Depends(get_sse_hub), # singleton on app.state
125
+ broker: SSEBroker = Depends(get_broker),
143
126
  ) -> StreamingResponse:
144
- """Subscribe the client to its user's feed."""
145
- stream = hub.register(user_id)
127
+ """Subscribe the client to its user's channel."""
128
+ channel = str(user_id)
129
+ stream = broker.register(channel)
146
130
 
147
131
  async def lifecycle_aware() -> AsyncIterator[bytes]:
148
132
  try:
149
133
  async for chunk in stream.stream():
150
134
  yield chunk
151
135
  finally:
152
- hub.unregister(user_id, stream)
136
+ broker.unregister(channel, stream) # client left
153
137
 
154
138
  return sse_response(lifecycle_aware())
155
139
 
156
140
 
157
141
  # From anywhere (queue handler, another endpoint):
158
- # await hub.publish_to_user(user_id, {"text": "New order"}, event="notice")
142
+ # await broker.publish(str(user_id), {"text": "New order"}, event="notice")
143
+ ```
144
+
145
+ ### Multi-worker: Redis bridge (ready, no extra code)
146
+
147
+ An in-memory `SSEBroker` lives in **one** worker — with `--workers N` a
148
+ `publish` only reaches the clients pinned to that process. Pass a Redis
149
+ client and the **same `broker`** publishes via Redis `PUBLISH`; a
150
+ background task (`run()`) `PSUBSCRIBE`-s and relays to **each** worker's
151
+ local streams. Same call site, now horizontal:
152
+
153
+ ```python
154
+ # src/api/app.py
155
+ import asyncio
156
+ from contextlib import asynccontextmanager
157
+
158
+ from fastapi import FastAPI
159
+ from redis.asyncio import Redis
160
+
161
+ from tempest_fastapi_sdk import SSEBroker
162
+
163
+ redis = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
164
+ broker = SSEBroker(redis=redis, channel_prefix="sse")
165
+
166
+
167
+ @asynccontextmanager
168
+ async def lifespan(app: FastAPI):
169
+ task = asyncio.create_task(broker.run()) # subscribe Redis + fan out
170
+ try:
171
+ yield
172
+ finally:
173
+ await broker.aclose()
174
+ task.cancel()
175
+
176
+
177
+ app = FastAPI(lifespan=lifespan)
178
+ # broker.publish(...) on any worker -> reaches ALL workers
159
179
  ```
160
180
 
161
- !!! danger "In-memory hub = single process"
162
- The `SSEHub` above lives in **one** worker's memory. With multiple
163
- workers (Gunicorn/Uvicorn `--workers N`), a publish only reaches the
164
- clients pinned to that process. For multi-process, back the hub with a
165
- Pub/Sub (Redis `PUBLISH`/`SUBSCRIBE`): each worker subscribes to the
166
- channel and relays to its local streams.
181
+ !!! tip "Start simple, scale later"
182
+ Without Redis, `SSEBroker()` already covers a single process. When you
183
+ need multiple workers/hosts, just inject a Redis client and start
184
+ `run()` in the lifespan no endpoint changes. `publish` becomes
185
+ cross-process for free.
167
186
 
168
187
  ## Aligned with tempest-react-sdk
169
188
 
@@ -202,5 +221,5 @@ Alignment points:
202
221
  - Tie the producer to the connection lifecycle (`finally` → cancel/unregister).
203
222
  - `publish(data, event=, id=, retry=)` covers the 4 spec fields; non-string `data` becomes JSON.
204
223
  - Heartbeat is a comment (invisible to EventSource); `None` disables it.
205
- - Broadcast = a registry of streams (hub); multi-worker needs Pub/Sub (Redis).
224
+ - Broadcast = `SSEBroker` (per-channel stream registry); multi-worker = pass a Redis client + start `broker.run()` in the lifespan (same call site).
206
225
  - `tempest-react-sdk` `createEventStream`/`useEventStream` consumes with reconnect; `namedEvents` ↔ `publish(event=)`.
@@ -93,77 +93,96 @@ stream fica ocioso, pra load-balancers não cortarem a conexão.
93
93
  Comentários são **invisíveis** ao `EventSource` — não disparam nenhum
94
94
  listener, só mantêm o socket vivo. `None` desliga o heartbeat.
95
95
 
96
- ## Broadcast pra vários clientes
96
+ ## Broadcast pra vários clientes (`SSEBroker`)
97
97
 
98
- `EventStream` é uma conexão. Pra mandar o mesmo evento pra todos (ou pros
99
- devices de um usuário), mantenha um registro de streams e publique em
100
- todosum "hub" simples:
98
+ `EventStream` é **uma** conexão. Pra mandar o mesmo evento pra todos os
99
+ clientes de um canal (ex.: os devices de um usuário, ou um tópico), o SDK
100
+ traz o `SSEBroker` registro de streams por canal + fan-out. O canal é
101
+ uma string qualquer (id de usuário, slug de sala...).
101
102
 
102
103
  ```python
103
- # src/services/sse_hub.py
104
- from uuid import UUID
105
-
106
- from tempest_fastapi_sdk import EventStream
104
+ # src/api/dependencies/resources.py
105
+ from tempest_fastapi_sdk import SSEBroker
107
106
 
107
+ broker = SSEBroker() # singleton — guarde em app.state e injete via Depends
108
+ ```
108
109
 
109
- class SSEHub:
110
- """Registro em memória de streams SSE abertos, por usuário."""
110
+ ```python
111
+ # src/api/routers/feed.py
112
+ from uuid import UUID
111
113
 
112
- def __init__(self) -> None:
113
- self._streams: dict[UUID, set[EventStream]] = {}
114
+ from fastapi import APIRouter, Depends
115
+ from starlette.responses import StreamingResponse
114
116
 
115
- def register(self, user_id: UUID) -> EventStream:
116
- """Abre um stream para um cliente e o registra."""
117
- stream = EventStream(heartbeat_seconds=15.0)
118
- self._streams.setdefault(user_id, set()).add(stream)
119
- return stream
117
+ from tempest_fastapi_sdk import sse_response
120
118
 
121
- def unregister(self, user_id: UUID, stream: EventStream) -> None:
122
- """Remove um stream fechado do registro."""
123
- streams = self._streams.get(user_id)
124
- if streams:
125
- streams.discard(stream)
126
- if not streams:
127
- del self._streams[user_id]
119
+ router = APIRouter()
128
120
 
129
- async def publish_to_user(self, user_id: UUID, data: object, *, event: str) -> int:
130
- """Publica um evento em todos os streams abertos de um usuário."""
131
- streams = self._streams.get(user_id, set())
132
- for stream in streams:
133
- await stream.publish(data, event=event)
134
- return len(streams)
135
- ```
136
121
 
137
- ```python
138
- # src/api/routers/feed.py
139
122
  @router.get("/feed")
140
123
  async def feed(
141
124
  user_id: UUID = Depends(get_current_user_id),
142
- hub: SSEHub = Depends(get_sse_hub), # singleton no app.state
125
+ broker: SSEBroker = Depends(get_broker),
143
126
  ) -> StreamingResponse:
144
- """Inscreve o cliente no feed do seu usuário."""
145
- stream = hub.register(user_id)
127
+ """Inscreve o cliente no canal do seu usuário."""
128
+ channel = str(user_id)
129
+ stream = broker.register(channel)
146
130
 
147
131
  async def lifecycle_aware() -> AsyncIterator[bytes]:
148
132
  try:
149
133
  async for chunk in stream.stream():
150
134
  yield chunk
151
135
  finally:
152
- hub.unregister(user_id, stream)
136
+ broker.unregister(channel, stream) # cliente saiu
153
137
 
154
138
  return sse_response(lifecycle_aware())
155
139
 
156
140
 
157
141
  # De qualquer lugar (handler de fila, outro endpoint):
158
- # await hub.publish_to_user(user_id, {"text": "Novo pedido"}, event="notice")
142
+ # await broker.publish(str(user_id), {"text": "Novo pedido"}, event="notice")
143
+ ```
144
+
145
+ ### Multi-worker: bridge Redis (pronto, sem código extra)
146
+
147
+ O `SSEBroker` em memória vive em **um** worker — com `--workers N`, um
148
+ `publish` só alcança os clientes presos naquele processo. Passe um client
149
+ Redis e o **mesmo `broker`** passa a publicar via Redis `PUBLISH`; uma
150
+ task de fundo (`run()`) faz `PSUBSCRIBE` e repassa pros streams locais de
151
+ **cada** worker. Mesmo call site, agora horizontal:
152
+
153
+ ```python
154
+ # src/api/app.py
155
+ import asyncio
156
+ from contextlib import asynccontextmanager
157
+
158
+ from fastapi import FastAPI
159
+ from redis.asyncio import Redis
160
+
161
+ from tempest_fastapi_sdk import SSEBroker
162
+
163
+ redis = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
164
+ broker = SSEBroker(redis=redis, channel_prefix="sse")
165
+
166
+
167
+ @asynccontextmanager
168
+ async def lifespan(app: FastAPI):
169
+ task = asyncio.create_task(broker.run()) # assina o Redis e faz o fan-out
170
+ try:
171
+ yield
172
+ finally:
173
+ await broker.aclose()
174
+ task.cancel()
175
+
176
+
177
+ app = FastAPI(lifespan=lifespan)
178
+ # broker.publish(...) em qualquer worker -> chega em TODOS os workers
159
179
  ```
160
180
 
161
- !!! danger "Hub em memória = um processo só"
162
- O `SSEHub` acima vive na memória de **um** worker. Com vários workers
163
- (Gunicorn/Uvicorn `--workers N`), um publish alcança os clientes
164
- presos naquele processo. Pra multi-processo, ligue o hub a um
165
- Pub/Sub (Redis `PUBLISH`/`SUBSCRIBE`): cada worker assina o canal e
166
- repassa pros seus streams locais.
181
+ !!! tip "Comece simples, escale depois"
182
+ Sem Redis, `SSEBroker()` resolve um processo. Quando precisar de
183
+ múltiplos workers/hosts, injete o client Redis e suba o `run()` no
184
+ lifespan nenhum endpoint muda. O `publish` se torna cross-process de
185
+ graça.
167
186
 
168
187
  ## Alinhado com o tempest-react-sdk
169
188
 
@@ -202,5 +221,5 @@ Pontos de alinhamento:
202
221
  - Amarre o produtor ao ciclo de vida da conexão (`finally` → cancela/desregistra).
203
222
  - `publish(data, event=, id=, retry=)` cobre os 4 campos do spec; `data` não-string vira JSON.
204
223
  - Heartbeat é comentário (invisível ao EventSource); `None` desliga.
205
- - Broadcast = registro de streams (hub); multi-worker exige Pub/Sub (Redis).
224
+ - Broadcast = `SSEBroker` (registro de streams por canal); multi-worker = passe um client Redis + suba `broker.run()` no lifespan (mesmo call site).
206
225
  - `tempest-react-sdk` `createEventStream`/`useEventStream` consome com reconnect; `namedEvents` ↔ `publish(event=)`.
@@ -241,6 +241,7 @@ Gerada automaticamente a partir das docstrings do SDK via [`mkdocstrings`](https
241
241
  ### `tempest_fastapi_sdk.sse`
242
242
 
243
243
  ::: tempest_fastapi_sdk.sse.event_stream.EventStream
244
+ ::: tempest_fastapi_sdk.sse.broker.SSEBroker
244
245
  ::: tempest_fastapi_sdk.sse.event_stream.ServerSentEvent
245
246
  ::: tempest_fastapi_sdk.sse.event_stream.sse_response
246
247
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempest-fastapi-sdk"
3
- version = "0.81.2"
3
+ version = "0.82.0"
4
4
  description = "Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -233,7 +233,12 @@ from tempest_fastapi_sdk.settings import (
233
233
  WebPushSettings,
234
234
  WebSocketSettings,
235
235
  )
236
- from tempest_fastapi_sdk.sse import EventStream, ServerSentEvent, sse_response
236
+ from tempest_fastapi_sdk.sse import (
237
+ EventStream,
238
+ ServerSentEvent,
239
+ SSEBroker,
240
+ sse_response,
241
+ )
237
242
  from tempest_fastapi_sdk.storage import AsyncMinIOClient, ObjectStat
238
243
  from tempest_fastapi_sdk.utils import (
239
244
  CEP,
@@ -346,7 +351,7 @@ from tempest_fastapi_sdk.websockets import (
346
351
  make_websocket_router,
347
352
  )
348
353
 
349
- __version__: str = "0.81.2"
354
+ __version__: str = "0.82.0"
350
355
 
351
356
  __all__: list[str] = [
352
357
  "BASE_COLUMN_ORDER",
@@ -517,6 +522,7 @@ __all__: list[str] = [
517
522
  "Region",
518
523
  "RequestIDMiddleware",
519
524
  "RetryPolicy",
525
+ "SSEBroker",
520
526
  "SameSite",
521
527
  "ServerSentEvent",
522
528
  "ServerSettings",
@@ -1,5 +1,6 @@
1
1
  """Server-Sent Events helpers built on top of Starlette streaming."""
2
2
 
3
+ from tempest_fastapi_sdk.sse.broker import SSEBroker
3
4
  from tempest_fastapi_sdk.sse.event_stream import (
4
5
  EventStream,
5
6
  ServerSentEvent,
@@ -8,6 +9,7 @@ from tempest_fastapi_sdk.sse.event_stream import (
8
9
 
9
10
  __all__: list[str] = [
10
11
  "EventStream",
12
+ "SSEBroker",
11
13
  "ServerSentEvent",
12
14
  "sse_response",
13
15
  ]
@@ -0,0 +1,208 @@
1
+ """Multi-worker SSE fan-out: local registry + optional Redis pub/sub bridge.
2
+
3
+ :class:`EventStream` feeds **one** connection. To broadcast the same
4
+ event to every client subscribed to a channel — across multiple
5
+ Uvicorn/Gunicorn workers — you need a fan-out layer plus a cross-process
6
+ transport. :class:`SSEBroker` is both:
7
+
8
+ * **Single process** (``redis=None``) — keeps a registry of local
9
+ streams per channel; :meth:`publish` fans out to them in-process.
10
+ * **Multi-worker** (``redis=<client>``) — :meth:`publish` goes through
11
+ Redis ``PUBLISH``; a background :meth:`run` task ``PSUBSCRIBE``-s the
12
+ channel prefix and re-fans every message to the worker's local
13
+ streams. Run :meth:`run` from the app lifespan and stop it with
14
+ :meth:`aclose`.
15
+
16
+ The same call site (``register`` / ``publish`` / ``unregister``) works in
17
+ both modes, so a service starts single-process and gains horizontal
18
+ scale by passing a Redis client — no code change.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ from typing import TYPE_CHECKING, Any
25
+
26
+ from tempest_fastapi_sdk.sse.event_stream import EventStream, ServerSentEvent
27
+
28
+ if TYPE_CHECKING:
29
+ from redis.asyncio import Redis
30
+
31
+
32
+ class SSEBroker:
33
+ """Channel-based SSE fan-out, optionally bridged over Redis pub/sub.
34
+
35
+ Attributes:
36
+ channel_prefix (str): Redis key prefix for published channels
37
+ (``"<prefix>:<channel>"``). Also the ``PSUBSCRIBE`` pattern
38
+ root. Ignored in single-process mode.
39
+ heartbeat_seconds (float | None): Idle interval passed to every
40
+ :class:`EventStream` created by :meth:`register`.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ *,
46
+ redis: Redis | None = None,
47
+ channel_prefix: str = "sse",
48
+ heartbeat_seconds: float | None = 15.0,
49
+ ) -> None:
50
+ """Initialize the broker.
51
+
52
+ Args:
53
+ redis (Redis | None): Async Redis client (from the ``[cache]``
54
+ extra). ``None`` runs single-process (local fan-out only).
55
+ channel_prefix (str): Prefix for Redis channels.
56
+ heartbeat_seconds (float | None): Idle heartbeat for streams.
57
+ """
58
+ self._redis: Redis | None = redis
59
+ self.channel_prefix: str = channel_prefix
60
+ self.heartbeat_seconds: float | None = heartbeat_seconds
61
+ self._channels: dict[str, set[EventStream]] = {}
62
+ self._pubsub: Any = None
63
+
64
+ def register(self, channel: str) -> EventStream:
65
+ """Open a local stream subscribed to ``channel`` and track it.
66
+
67
+ Args:
68
+ channel (str): The logical channel (e.g. a user id or topic).
69
+
70
+ Returns:
71
+ EventStream: A fresh stream to hand to
72
+ :func:`tempest_fastapi_sdk.sse_response`.
73
+ """
74
+ stream = EventStream(heartbeat_seconds=self.heartbeat_seconds)
75
+ self._channels.setdefault(channel, set()).add(stream)
76
+ return stream
77
+
78
+ def unregister(self, channel: str, stream: EventStream) -> None:
79
+ """Drop a closed stream from ``channel``.
80
+
81
+ Call this from the response generator's ``finally`` so a
82
+ disconnected client is forgotten.
83
+
84
+ Args:
85
+ channel (str): The channel the stream was registered under.
86
+ stream (EventStream): The stream to remove.
87
+ """
88
+ streams = self._channels.get(channel)
89
+ if streams:
90
+ streams.discard(stream)
91
+ if not streams:
92
+ del self._channels[channel]
93
+
94
+ def local_subscribers(self, channel: str) -> int:
95
+ """Return how many local streams are open on ``channel``.
96
+
97
+ Args:
98
+ channel (str): The channel to inspect.
99
+
100
+ Returns:
101
+ int: The local subscriber count (this worker only).
102
+ """
103
+ return len(self._channels.get(channel, set()))
104
+
105
+ async def publish(
106
+ self,
107
+ channel: str,
108
+ data: Any = "",
109
+ *,
110
+ event: str | None = None,
111
+ id: str | None = None,
112
+ retry: int | None = None,
113
+ ) -> None:
114
+ """Publish an event to every subscriber of ``channel``.
115
+
116
+ In Redis mode the event is ``PUBLISH``-ed and delivered to all
117
+ workers (including this one) by their :meth:`run` loop. In
118
+ single-process mode it fans out to local streams immediately.
119
+
120
+ Args:
121
+ channel (str): Target channel.
122
+ data (Any): Payload (string, bytes or JSON-serializable).
123
+ event (str | None): Optional SSE event name.
124
+ id (str | None): Optional ``Last-Event-ID``.
125
+ retry (int | None): Optional reconnect hint (ms).
126
+ """
127
+ if self._redis is None:
128
+ await self._emit_local(
129
+ channel,
130
+ ServerSentEvent(data=data, event=event, id=id, retry=retry),
131
+ )
132
+ return
133
+ envelope = json.dumps(
134
+ {"data": data, "event": event, "id": id, "retry": retry},
135
+ default=str,
136
+ )
137
+ await self._redis.publish(self._key(channel), envelope)
138
+
139
+ async def run(self) -> None:
140
+ """Consume Redis pub/sub and fan messages to local streams.
141
+
142
+ Long-running: start it as a background task in the app lifespan
143
+ and cancel it (or call :meth:`aclose`) on shutdown. A no-op in
144
+ single-process mode.
145
+
146
+ Raises:
147
+ RuntimeError: If called without a Redis client.
148
+ """
149
+ if self._redis is None:
150
+ raise RuntimeError("SSEBroker.run() requires a Redis client.")
151
+ pubsub: Any = self._redis.pubsub()
152
+ self._pubsub = pubsub
153
+ await pubsub.psubscribe(f"{self.channel_prefix}:*")
154
+ try:
155
+ async for message in pubsub.listen():
156
+ if message.get("type") != "pmessage":
157
+ continue
158
+ await self._dispatch_raw(message["channel"], message["data"])
159
+ finally:
160
+ await pubsub.aclose()
161
+ self._pubsub = None
162
+
163
+ async def aclose(self) -> None:
164
+ """Stop the pub/sub subscription opened by :meth:`run`."""
165
+ if self._pubsub is not None:
166
+ await self._pubsub.aclose()
167
+ self._pubsub = None
168
+
169
+ def _key(self, channel: str) -> str:
170
+ """Return the Redis channel key for ``channel``."""
171
+ return f"{self.channel_prefix}:{channel}"
172
+
173
+ async def _dispatch_raw(self, channel_key: str | bytes, raw: str | bytes) -> None:
174
+ """Decode a Redis pub/sub frame and fan it to local streams.
175
+
176
+ Args:
177
+ channel_key (str | bytes): The Redis channel
178
+ (``<prefix>:<channel>``), as delivered by pub/sub.
179
+ raw (str | bytes): The JSON envelope payload.
180
+ """
181
+ key = channel_key.decode() if isinstance(channel_key, bytes) else channel_key
182
+ body = raw.decode() if isinstance(raw, bytes) else raw
183
+ channel = key.removeprefix(f"{self.channel_prefix}:")
184
+ payload: dict[str, Any] = json.loads(body)
185
+ await self._emit_local(
186
+ channel,
187
+ ServerSentEvent(
188
+ data=payload.get("data", ""),
189
+ event=payload.get("event"),
190
+ id=payload.get("id"),
191
+ retry=payload.get("retry"),
192
+ ),
193
+ )
194
+
195
+ async def _emit_local(self, channel: str, event: ServerSentEvent) -> None:
196
+ """Enqueue ``event`` on every local stream of ``channel``.
197
+
198
+ Args:
199
+ channel (str): The channel to fan out to.
200
+ event (ServerSentEvent): The event to deliver.
201
+ """
202
+ for stream in tuple(self._channels.get(channel, set())):
203
+ await stream.publish_event(event)
204
+
205
+
206
+ __all__: list[str] = [
207
+ "SSEBroker",
208
+ ]