svc-infra 0.1.622__tar.gz → 0.1.623__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.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

Files changed (330) hide show
  1. {svc_infra-0.1.622 → svc_infra-0.1.623}/PKG-INFO +16 -16
  2. {svc_infra-0.1.622 → svc_infra-0.1.623}/README.md +15 -15
  3. {svc_infra-0.1.622 → svc_infra-0.1.623}/pyproject.toml +2 -2
  4. svc_infra-0.1.623/src/svc_infra/cli/cmds/docs/docs_cmds.py +189 -0
  5. svc_infra-0.1.622/src/svc_infra/cli/cmds/docs/docs_cmds.py +0 -225
  6. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/__init__.py +0 -0
  7. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/README.md +0 -0
  8. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/__init__.py +0 -0
  9. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/alembic.py +0 -0
  10. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/models.py +0 -0
  11. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/provider/__init__.py +0 -0
  12. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/provider/aiydan.py +0 -0
  13. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/provider/base.py +0 -0
  14. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/provider/registry.py +0 -0
  15. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/provider/stripe.py +0 -0
  16. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/schemas.py +0 -0
  17. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/service.py +0 -0
  18. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/apf_payments/settings.py +0 -0
  19. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/__init__.py +0 -0
  20. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/__init__.py +0 -0
  21. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/apf_payments/__init__.py +0 -0
  22. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/apf_payments/router.py +0 -0
  23. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/apf_payments/setup.py +0 -0
  24. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/__init__.py +0 -0
  25. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/_cookies.py +0 -0
  26. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/add.py +0 -0
  27. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/gaurd.py +0 -0
  28. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/__init__.py +0 -0
  29. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/models.py +0 -0
  30. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/pre_auth.py +0 -0
  31. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/router.py +0 -0
  32. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/security.py +0 -0
  33. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/utils.py +0 -0
  34. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/mfa/verify.py +0 -0
  35. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/policy.py +0 -0
  36. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/providers.py +0 -0
  37. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/routers/__init__.py +0 -0
  38. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/routers/account.py +0 -0
  39. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/routers/apikey_router.py +0 -0
  40. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/routers/oauth_router.py +0 -0
  41. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/routers/session_router.py +0 -0
  42. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/security.py +0 -0
  43. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/sender.py +0 -0
  44. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/settings.py +0 -0
  45. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/auth/state.py +0 -0
  46. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/cache/__init__.py +0 -0
  47. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/cache/add.py +0 -0
  48. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/__init__.py +0 -0
  49. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/http.py +0 -0
  50. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/nosql/__init__.py +0 -0
  51. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/nosql/mongo/__init__.py +0 -0
  52. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/nosql/mongo/add.py +0 -0
  53. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/nosql/mongo/crud_router.py +0 -0
  54. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/nosql/mongo/health.py +0 -0
  55. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/README.md +0 -0
  56. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/__init__.py +0 -0
  57. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/add.py +0 -0
  58. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/crud_router.py +0 -0
  59. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/health.py +0 -0
  60. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/session.py +0 -0
  61. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/db/sql/users.py +0 -0
  62. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dependencies/ratelimit.py +0 -0
  63. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/docs/__init__.py +0 -0
  64. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/docs/add.py +0 -0
  65. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/docs/landing.py +0 -0
  66. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/docs/scoped.py +0 -0
  67. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/__init__.py +0 -0
  68. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/dualize.py +0 -0
  69. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/protected.py +0 -0
  70. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/public.py +0 -0
  71. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/router.py +0 -0
  72. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dual/utils.py +0 -0
  73. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/dx.py +0 -0
  74. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/ease.py +0 -0
  75. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/http/__init__.py +0 -0
  76. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/http/concurrency.py +0 -0
  77. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/http/conditional.py +0 -0
  78. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/http/deprecation.py +0 -0
  79. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/__init__.py +0 -0
  80. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/debug.py +0 -0
  81. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/errors/__init__.py +0 -0
  82. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/errors/catchall.py +0 -0
  83. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/errors/exceptions.py +0 -0
  84. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/errors/handlers.py +0 -0
  85. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/idempotency.py +0 -0
  86. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/idempotency_store.py +0 -0
  87. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/optimistic_lock.py +0 -0
  88. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/ratelimit.py +0 -0
  89. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/ratelimit_store.py +0 -0
  90. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/request_id.py +0 -0
  91. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/middleware/request_size_limit.py +0 -0
  92. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/__init__.py +0 -0
  93. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/apply.py +0 -0
  94. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/conventions.py +0 -0
  95. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/models.py +0 -0
  96. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/mutators.py +0 -0
  97. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/pipeline.py +0 -0
  98. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/responses.py +0 -0
  99. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/openapi/security.py +0 -0
  100. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/ops/add.py +0 -0
  101. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/pagination.py +0 -0
  102. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/paths/__init__.py +0 -0
  103. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/paths/auth.py +0 -0
  104. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/paths/generic.py +0 -0
  105. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/paths/prefix.py +0 -0
  106. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/paths/user.py +0 -0
  107. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/routers/__init__.py +0 -0
  108. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/routers/ping.py +0 -0
  109. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/setup.py +0 -0
  110. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/tenancy/add.py +0 -0
  111. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/api/fastapi/tenancy/context.py +0 -0
  112. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/README.md +0 -0
  113. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/__init__.py +0 -0
  114. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/env.py +0 -0
  115. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/logging/__init__.py +0 -0
  116. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/logging/add.py +0 -0
  117. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/logging/filter.py +0 -0
  118. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/logging/formats.py +0 -0
  119. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/app/root.py +0 -0
  120. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/billing/__init__.py +0 -0
  121. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/billing/models.py +0 -0
  122. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/billing/service.py +0 -0
  123. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/bundled_docs/README.md +0 -0
  124. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/bundled_docs/__init__.py +0 -0
  125. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/bundled_docs/getting-started.md +0 -0
  126. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/README.md +0 -0
  127. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/__init__.py +0 -0
  128. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/backend.py +0 -0
  129. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/decorators.py +0 -0
  130. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/demo.py +0 -0
  131. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/keys.py +0 -0
  132. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/recache.py +0 -0
  133. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/resources.py +0 -0
  134. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/tags.py +0 -0
  135. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/ttl.py +0 -0
  136. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cache/utils.py +0 -0
  137. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/__init__.py +0 -0
  138. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/__main__.py +0 -0
  139. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/__init__.py +0 -0
  140. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/__init__.py +0 -0
  141. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/nosql/__init__.py +0 -0
  142. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/nosql/mongo/README.md +0 -0
  143. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/nosql/mongo/__init__.py +0 -0
  144. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_cmds.py +0 -0
  145. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/nosql/mongo/mongo_scaffold_cmds.py +0 -0
  146. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/sql/__init__.py +0 -0
  147. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/sql/alembic_cmds.py +0 -0
  148. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/sql/sql_export_cmds.py +0 -0
  149. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py +0 -0
  150. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/dx/__init__.py +0 -0
  151. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/dx/dx_cmds.py +0 -0
  152. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/help.py +0 -0
  153. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/jobs/__init__.py +0 -0
  154. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/jobs/jobs_cmds.py +0 -0
  155. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/obs/__init__.py +0 -0
  156. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/obs/obs_cmds.py +0 -0
  157. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/sdk/__init__.py +0 -0
  158. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/cmds/sdk/sdk_cmds.py +0 -0
  159. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/foundation/__init__.py +0 -0
  160. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/foundation/runner.py +0 -0
  161. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/cli/foundation/typer_bootstrap.py +0 -0
  162. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/data/add.py +0 -0
  163. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/data/backup.py +0 -0
  164. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/data/erasure.py +0 -0
  165. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/data/fixtures.py +0 -0
  166. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/data/retention.py +0 -0
  167. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/__init__.py +0 -0
  168. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/crud_schema.py +0 -0
  169. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/inbox.py +0 -0
  170. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/__init__.py +0 -0
  171. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/base.py +0 -0
  172. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/constants.py +0 -0
  173. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/core.py +0 -0
  174. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/indexes.py +0 -0
  175. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/management.py +0 -0
  176. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/README.md +0 -0
  177. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/__init__.py +0 -0
  178. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/client.py +0 -0
  179. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/settings.py +0 -0
  180. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/templates/__init__.py +0 -0
  181. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/templates/documents.py.tmpl +0 -0
  182. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/templates/resources.py.tmpl +0 -0
  183. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/mongo/templates/schemas.py.tmpl +0 -0
  184. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/repository.py +0 -0
  185. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/resource.py +0 -0
  186. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/scaffold.py +0 -0
  187. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/service.py +0 -0
  188. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/service_with_hooks.py +0 -0
  189. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/types.py +0 -0
  190. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/nosql/utils.py +0 -0
  191. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/outbox.py +0 -0
  192. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/README.md +0 -0
  193. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/__init__.py +0 -0
  194. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/apikey.py +0 -0
  195. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/authref.py +0 -0
  196. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/base.py +0 -0
  197. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/constants.py +0 -0
  198. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/core.py +0 -0
  199. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/management.py +0 -0
  200. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/repository.py +0 -0
  201. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/resource.py +0 -0
  202. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/scaffold.py +0 -0
  203. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/service.py +0 -0
  204. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/service_with_hooks.py +0 -0
  205. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/__init__.py +0 -0
  206. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/models_schemas/__init__.py +0 -0
  207. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/models_schemas/auth/models.py.tmpl +0 -0
  208. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/models_schemas/auth/schemas.py.tmpl +0 -0
  209. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/models_schemas/entity/models.py.tmpl +0 -0
  210. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/models_schemas/entity/schemas.py.tmpl +0 -0
  211. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/setup/__init__.py +0 -0
  212. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/setup/alembic.ini.tmpl +0 -0
  213. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/setup/env_async.py.tmpl +0 -0
  214. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/setup/env_sync.py.tmpl +0 -0
  215. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/templates/setup/script.py.mako.tmpl +0 -0
  216. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/tenant.py +0 -0
  217. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/types.py +0 -0
  218. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/uniq.py +0 -0
  219. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/uniq_hooks.py +0 -0
  220. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/utils.py +0 -0
  221. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/sql/versioning.py +0 -0
  222. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/db/utils.py +0 -0
  223. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/acceptance-matrix.md +0 -0
  224. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/acceptance.md +0 -0
  225. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0002-background-jobs-and-scheduling.md +0 -0
  226. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0003-webhooks-framework.md +0 -0
  227. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0004-tenancy-model.md +0 -0
  228. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0005-data-lifecycle.md +0 -0
  229. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0006-ops-slos-and-metrics.md +0 -0
  230. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0007-docs-and-sdks.md +0 -0
  231. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0008-billing-primitives.md +0 -0
  232. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/adr/0009-acceptance-harness.md +0 -0
  233. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/api.md +0 -0
  234. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/auth.md +0 -0
  235. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/cache.md +0 -0
  236. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/cli.md +0 -0
  237. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/contributing.md +0 -0
  238. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/data-lifecycle.md +0 -0
  239. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/database.md +0 -0
  240. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/docs-and-sdks.md +0 -0
  241. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/environment.md +0 -0
  242. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/idempotency.md +0 -0
  243. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/jobs.md +0 -0
  244. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/observability.md +0 -0
  245. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/ops.md +0 -0
  246. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/rate-limiting.md +0 -0
  247. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/repo-review.md +0 -0
  248. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/security.md +0 -0
  249. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/tenancy.md +0 -0
  250. {svc_infra-0.1.622 → svc_infra-0.1.623/src/svc_infra}/docs/webhooks.md +0 -0
  251. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/dx/add.py +0 -0
  252. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/dx/changelog.py +0 -0
  253. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/dx/checks.py +0 -0
  254. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/builtins/outbox_processor.py +0 -0
  255. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/builtins/webhook_delivery.py +0 -0
  256. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/easy.py +0 -0
  257. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/loader.py +0 -0
  258. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/queue.py +0 -0
  259. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/redis_queue.py +0 -0
  260. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/scheduler.py +0 -0
  261. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/jobs/worker.py +0 -0
  262. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/mcp/__init__.py +0 -0
  263. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/mcp/svc_infra_mcp.py +0 -0
  264. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/README.md +0 -0
  265. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/__init__.py +0 -0
  266. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/add.py +0 -0
  267. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/cloud_dash.py +0 -0
  268. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/grafana/dashboards/http-overview.json +0 -0
  269. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics/__init__.py +0 -0
  270. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics/asgi.py +0 -0
  271. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics/base.py +0 -0
  272. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics/http.py +0 -0
  273. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics/sqlalchemy.py +0 -0
  274. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/metrics.py +0 -0
  275. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/__init__.py +0 -0
  276. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/compose_cloud/__init__.py +0 -0
  277. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/compose_cloud/templates/__init__.py +0 -0
  278. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/compose_cloud/templates/agent.yaml.tmpl +0 -0
  279. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/compose_cloud/templates/docker-compose.cloud.yml.tmpl +0 -0
  280. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/__init__.py +0 -0
  281. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/00_overview.json +0 -0
  282. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/10_http.json +0 -0
  283. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/20_db.json +0 -0
  284. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/30_runtime.json +0 -0
  285. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/40_clients.json +0 -0
  286. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/dashboards/__init__.py +0 -0
  287. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/__init__.py +0 -0
  288. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/docker-compose.yml.tmpl +0 -0
  289. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/prometheus.yml.tmpl +0 -0
  290. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/provisioning/__init__.py +0 -0
  291. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/provisioning/dashboards.yml +0 -0
  292. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/providers/grafana/templates/provisioning/datasource.yml +0 -0
  293. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/settings.py +0 -0
  294. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/grafana_dashboard.json +0 -0
  295. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/prometheus_rules.yml +0 -0
  296. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/__init__.py +0 -0
  297. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/compose/__init__.py +0 -0
  298. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/compose/agent.yaml +0 -0
  299. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/compose/docker-compose.yml +0 -0
  300. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/fly/__init__.py +0 -0
  301. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/fly/agent.yaml +0 -0
  302. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/fly/fly.toml.fragment +0 -0
  303. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/k8s/__init__.py +0 -0
  304. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/k8s/configmap.yaml +0 -0
  305. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/k8s/deployment.yaml +0 -0
  306. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/railway/Dockerfile +0 -0
  307. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/railway/README.md +0 -0
  308. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/railway/__init__.py +0 -0
  309. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/obs/templates/sidecars/railway/agent.yaml +0 -0
  310. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/py.typed +0 -0
  311. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/add.py +0 -0
  312. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/audit.py +0 -0
  313. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/audit_service.py +0 -0
  314. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/headers.py +0 -0
  315. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/hibp.py +0 -0
  316. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/jwt_rotation.py +0 -0
  317. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/lockout.py +0 -0
  318. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/models.py +0 -0
  319. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/org_invites.py +0 -0
  320. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/passwords.py +0 -0
  321. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/permissions.py +0 -0
  322. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/session.py +0 -0
  323. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/security/signed_cookies.py +0 -0
  324. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/utils.py +0 -0
  325. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/__init__.py +0 -0
  326. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/add.py +0 -0
  327. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/fastapi.py +0 -0
  328. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/router.py +0 -0
  329. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/service.py +0 -0
  330. {svc_infra-0.1.622 → svc_infra-0.1.623}/src/svc_infra/webhooks/signing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.622
3
+ Version: 0.1.623
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -77,7 +77,7 @@ Description-Content-Type: text/markdown
77
77
  # svc-infra
78
78
 
79
79
  [![PyPI](https://img.shields.io/pypi/v/svc-infra.svg)](https://pypi.org/project/svc-infra/)
80
- [![Docs](https://img.shields.io/badge/docs-reference-blue)](docs/)
80
+ [![Docs](https://img.shields.io/badge/docs-reference-blue)](src/svc_infra/docs/)
81
81
 
82
82
  svc-infra packages the shared building blocks we use to ship production FastAPI services fast—HTTP APIs with secure auth, durable persistence, background execution, cache, observability, and webhook plumbing that all share the same batteries-included defaults.
83
83
 
@@ -85,17 +85,17 @@ svc-infra packages the shared building blocks we use to ship production FastAPI
85
85
 
86
86
  | Helper | What it covers | Guide |
87
87
  | --- | --- | --- |
88
- | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](docs/api.md) |
89
- | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](docs/auth.md) |
90
- | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](docs/database.md) |
91
- | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](docs/jobs.md) |
92
- | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](docs/cache.md) |
93
- | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](docs/observability.md) |
94
- | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](docs/ops.md) |
95
- | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](docs/webhooks.md) |
96
- | Security | Password policy, lockout, signed cookies, headers | [Security hardening](docs/security.md) |
97
- | Contributing | Dev setup and quality gates | [Contributing guide](docs/contributing.md) |
98
- | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](docs/data-lifecycle.md) |
88
+ | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](src/svc_infra/docs/api.md) |
89
+ | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](src/svc_infra/docs/auth.md) |
90
+ | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](src/svc_infra/docs/database.md) |
91
+ | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](src/svc_infra/docs/jobs.md) |
92
+ | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](src/svc_infra/docs/cache.md) |
93
+ | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](src/svc_infra/docs/observability.md) |
94
+ | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](src/svc_infra/docs/ops.md) |
95
+ | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](src/svc_infra/docs/webhooks.md) |
96
+ | Security | Password policy, lockout, signed cookies, headers | [Security hardening](src/svc_infra/docs/security.md) |
97
+ | Contributing | Dev setup and quality gates | [Contributing guide](src/svc_infra/docs/contributing.md) |
98
+ | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](src/svc_infra/docs/data-lifecycle.md) |
99
99
 
100
100
  ## Minimal FastAPI bootstrap
101
101
 
@@ -123,9 +123,9 @@ async def handle_webhook(payload = Depends(require_signature(lambda: ["current",
123
123
  - **API** – toggle logging/observability and docs exposure with `ENABLE_LOGGING`, `LOG_LEVEL`, `LOG_FORMAT`, `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and `CORS_ALLOW_ORIGINS`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/api/fastapi/setup.py†L47-L88】
124
124
  - **Auth** – configure JWT secrets, SMTP, cookies, and policy using the `AUTH_…` settings family (e.g., `AUTH_JWT__SECRET`, `AUTH_SMTP_HOST`, `AUTH_SESSION_COOKIE_SECURE`). 【F:src/svc_infra/api/fastapi/auth/settings.py†L23-L91】
125
125
  - **Database** – set connection URLs or components via `SQL_URL`/`SQL_URL_FILE`, `DB_DIALECT`, `DB_HOST`, `DB_USER`, `DB_PASSWORD`, plus Mongo knobs like `MONGO_URL`, `MONGO_DB`, and `MONGO_URL_FILE`. 【F:src/svc_infra/api/fastapi/db/sql/add.py†L55-L114】【F:src/svc_infra/db/sql/utils.py†L85-L206】【F:src/svc_infra/db/nosql/mongo/settings.py†L9-L13】【F:src/svc_infra/db/nosql/utils.py†L56-L113】
126
- - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:docs/jobs.md†L11-L48】
126
+ - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:src/svc_infra/docs/jobs.md†L11-L48】
127
127
  - **Cache** – namespace keys and lifetimes through `CACHE_PREFIX`, `CACHE_VERSION`, and TTL overrides `CACHE_TTL_DEFAULT`, `CACHE_TTL_SHORT`, `CACHE_TTL_LONG`. 【F:src/svc_infra/cache/README.md†L20-L173】【F:src/svc_infra/cache/ttl.py†L26-L55】
128
128
  - **Observability** – turn metrics on/off or adjust scrape paths with `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and Prometheus/Grafana flags like `SVC_INFRA_DISABLE_PROMETHEUS`, `SVC_INFRA_RATE_WINDOW`, `SVC_INFRA_DASHBOARD_REFRESH`, `SVC_INFRA_DASHBOARD_RANGE`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/obs/metrics/asgi.py†L49-L206】【F:src/svc_infra/obs/cloud_dash.py†L85-L108】
129
- - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:docs/webhooks.md†L32-L53】
130
- - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:docs/security.md†L24-L70】
129
+ - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:src/svc_infra/docs/webhooks.md†L32-L53】
130
+ - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:src/svc_infra/docs/security.md†L24-L70】
131
131
 
@@ -1,7 +1,7 @@
1
1
  # svc-infra
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/svc-infra.svg)](https://pypi.org/project/svc-infra/)
4
- [![Docs](https://img.shields.io/badge/docs-reference-blue)](docs/)
4
+ [![Docs](https://img.shields.io/badge/docs-reference-blue)](src/svc_infra/docs/)
5
5
 
6
6
  svc-infra packages the shared building blocks we use to ship production FastAPI services fast—HTTP APIs with secure auth, durable persistence, background execution, cache, observability, and webhook plumbing that all share the same batteries-included defaults.
7
7
 
@@ -9,17 +9,17 @@ svc-infra packages the shared building blocks we use to ship production FastAPI
9
9
 
10
10
  | Helper | What it covers | Guide |
11
11
  | --- | --- | --- |
12
- | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](docs/api.md) |
13
- | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](docs/auth.md) |
14
- | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](docs/database.md) |
15
- | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](docs/jobs.md) |
16
- | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](docs/cache.md) |
17
- | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](docs/observability.md) |
18
- | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](docs/ops.md) |
19
- | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](docs/webhooks.md) |
20
- | Security | Password policy, lockout, signed cookies, headers | [Security hardening](docs/security.md) |
21
- | Contributing | Dev setup and quality gates | [Contributing guide](docs/contributing.md) |
22
- | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](docs/data-lifecycle.md) |
12
+ | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](src/svc_infra/docs/api.md) |
13
+ | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](src/svc_infra/docs/auth.md) |
14
+ | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](src/svc_infra/docs/database.md) |
15
+ | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](src/svc_infra/docs/jobs.md) |
16
+ | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](src/svc_infra/docs/cache.md) |
17
+ | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](src/svc_infra/docs/observability.md) |
18
+ | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](src/svc_infra/docs/ops.md) |
19
+ | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](src/svc_infra/docs/webhooks.md) |
20
+ | Security | Password policy, lockout, signed cookies, headers | [Security hardening](src/svc_infra/docs/security.md) |
21
+ | Contributing | Dev setup and quality gates | [Contributing guide](src/svc_infra/docs/contributing.md) |
22
+ | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](src/svc_infra/docs/data-lifecycle.md) |
23
23
 
24
24
  ## Minimal FastAPI bootstrap
25
25
 
@@ -47,8 +47,8 @@ async def handle_webhook(payload = Depends(require_signature(lambda: ["current",
47
47
  - **API** – toggle logging/observability and docs exposure with `ENABLE_LOGGING`, `LOG_LEVEL`, `LOG_FORMAT`, `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and `CORS_ALLOW_ORIGINS`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/api/fastapi/setup.py†L47-L88】
48
48
  - **Auth** – configure JWT secrets, SMTP, cookies, and policy using the `AUTH_…` settings family (e.g., `AUTH_JWT__SECRET`, `AUTH_SMTP_HOST`, `AUTH_SESSION_COOKIE_SECURE`). 【F:src/svc_infra/api/fastapi/auth/settings.py†L23-L91】
49
49
  - **Database** – set connection URLs or components via `SQL_URL`/`SQL_URL_FILE`, `DB_DIALECT`, `DB_HOST`, `DB_USER`, `DB_PASSWORD`, plus Mongo knobs like `MONGO_URL`, `MONGO_DB`, and `MONGO_URL_FILE`. 【F:src/svc_infra/api/fastapi/db/sql/add.py†L55-L114】【F:src/svc_infra/db/sql/utils.py†L85-L206】【F:src/svc_infra/db/nosql/mongo/settings.py†L9-L13】【F:src/svc_infra/db/nosql/utils.py†L56-L113】
50
- - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:docs/jobs.md†L11-L48】
50
+ - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:src/svc_infra/docs/jobs.md†L11-L48】
51
51
  - **Cache** – namespace keys and lifetimes through `CACHE_PREFIX`, `CACHE_VERSION`, and TTL overrides `CACHE_TTL_DEFAULT`, `CACHE_TTL_SHORT`, `CACHE_TTL_LONG`. 【F:src/svc_infra/cache/README.md†L20-L173】【F:src/svc_infra/cache/ttl.py†L26-L55】
52
52
  - **Observability** – turn metrics on/off or adjust scrape paths with `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and Prometheus/Grafana flags like `SVC_INFRA_DISABLE_PROMETHEUS`, `SVC_INFRA_RATE_WINDOW`, `SVC_INFRA_DASHBOARD_REFRESH`, `SVC_INFRA_DASHBOARD_RANGE`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/obs/metrics/asgi.py†L49-L206】【F:src/svc_infra/obs/cloud_dash.py†L85-L108】
53
- - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:docs/webhooks.md†L32-L53】
54
- - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:docs/security.md†L24-L70】
53
+ - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:src/svc_infra/docs/webhooks.md†L32-L53】
54
+ - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:src/svc_infra/docs/security.md†L24-L70】
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "svc-infra"
3
- version = "0.1.622"
3
+ version = "0.1.623"
4
4
  description = "Infrastructure for building and deploying prod-ready services"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -13,7 +13,7 @@ include = [
13
13
  "src/svc_infra/obs/templates/**/*",
14
14
  "src/svc_infra/obs/grafana/templates/**/*",
15
15
  "src/svc_infra/obs/grafana/dashboards/**/*",
16
- "docs/**/*"
16
+ "src/svc_infra/docs/**/*"
17
17
  ]
18
18
 
19
19
  classifiers = [
@@ -0,0 +1,189 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from importlib.resources import as_file
5
+ from importlib.resources import files as pkg_files
6
+ from pathlib import Path
7
+ from typing import Dict, List
8
+
9
+ import click
10
+ import typer
11
+ from typer.core import TyperGroup
12
+
13
+ from svc_infra.app.root import resolve_project_root
14
+
15
+
16
+ def _norm(name: str) -> str:
17
+ return name.strip().lower().replace(" ", "-").replace("_", "-")
18
+
19
+
20
+ def _discover_fs_topics(docs_dir: Path) -> Dict[str, Path]:
21
+ topics: Dict[str, Path] = {}
22
+ if docs_dir.exists() and docs_dir.is_dir():
23
+ for p in sorted(docs_dir.glob("*.md")):
24
+ if p.is_file():
25
+ topics[_norm(p.stem)] = p
26
+ return topics
27
+
28
+
29
+ def _discover_pkg_topics() -> Dict[str, Path]:
30
+ """
31
+ Discover docs shipped inside the installed package at svc_infra/docs/*,
32
+ using importlib.resources so this works for wheels, sdists, and zipped wheels.
33
+ """
34
+ topics: Dict[str, Path] = {}
35
+ try:
36
+ docs_root = pkg_files("svc_infra").joinpath("docs")
37
+ # docs_root is a Traversable; it may be inside a zip. Iterate safely.
38
+ for entry in docs_root.iterdir():
39
+ if entry.name.endswith(".md"):
40
+ # materialize to a real tempfile path if needed
41
+ with as_file(entry) as concrete:
42
+ p = Path(concrete)
43
+ if p.exists() and p.is_file():
44
+ topics[_norm(p.stem)] = p
45
+ except Exception:
46
+ # If the package has no docs directory, just return empty.
47
+ pass
48
+ return topics
49
+
50
+
51
+ def _resolve_docs_dir(ctx: click.Context) -> Path | None:
52
+ """
53
+ Optional override precedence:
54
+ 1) --docs-dir CLI option
55
+ 2) SVC_INFRA_DOCS_DIR env var
56
+ 3) *Only when working inside the svc-infra repo itself*: repo-root /docs
57
+ """
58
+ # 1) CLI option on this or parent contexts
59
+ current: click.Context | None = ctx
60
+ while current is not None:
61
+ docs_dir_opt = (current.params or {}).get("docs_dir")
62
+ if docs_dir_opt:
63
+ path = docs_dir_opt if isinstance(docs_dir_opt, Path) else Path(docs_dir_opt)
64
+ path = path.expanduser()
65
+ if path.exists():
66
+ return path
67
+ current = current.parent
68
+
69
+ # 2) Env var
70
+ env_dir = os.getenv("SVC_INFRA_DOCS_DIR")
71
+ if env_dir:
72
+ p = Path(env_dir).expanduser()
73
+ if p.exists():
74
+ return p
75
+
76
+ # 3) In-repo convenience (so `svc-infra docs` works inside this repo)
77
+ try:
78
+ root = resolve_project_root()
79
+ proj_docs = root / "docs"
80
+ if proj_docs.exists():
81
+ return proj_docs
82
+ except Exception:
83
+ pass
84
+
85
+ return None
86
+
87
+
88
+ class DocsGroup(TyperGroup):
89
+ def list_commands(self, ctx: click.Context) -> List[str]:
90
+ names: List[str] = list(super().list_commands(ctx) or [])
91
+ dir_to_use = _resolve_docs_dir(ctx)
92
+ fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
93
+ pkg = _discover_pkg_topics()
94
+ names.extend(fs.keys())
95
+ names.extend([k for k in pkg.keys() if k not in fs])
96
+ return sorted(set(names))
97
+
98
+ def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
99
+ cmd = super().get_command(ctx, name)
100
+ if cmd is not None:
101
+ return cmd
102
+
103
+ key = _norm(name)
104
+
105
+ dir_to_use = _resolve_docs_dir(ctx)
106
+ fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
107
+ if key in fs:
108
+ file_path = fs[key]
109
+
110
+ @click.command(name=name)
111
+ def _show_fs() -> None:
112
+ click.echo(file_path.read_text(encoding="utf-8", errors="replace"))
113
+
114
+ return _show_fs
115
+
116
+ pkg = _discover_pkg_topics()
117
+ if key in pkg:
118
+ file_path = pkg[key]
119
+
120
+ @click.command(name=name)
121
+ def _show_pkg() -> None:
122
+ click.echo(file_path.read_text(encoding="utf-8", errors="replace"))
123
+
124
+ return _show_pkg
125
+
126
+ return None
127
+
128
+
129
+ def register(app: typer.Typer) -> None:
130
+ docs_app = typer.Typer(no_args_is_help=True, add_completion=False, cls=DocsGroup)
131
+
132
+ @docs_app.callback(invoke_without_command=True)
133
+ def _docs_options(
134
+ docs_dir: Path | None = typer.Option(
135
+ None,
136
+ "--docs-dir",
137
+ help="Path to a docs directory to read from (overrides packaged docs)",
138
+ ),
139
+ topic: str | None = typer.Option(None, "--topic", help="Topic to show directly"),
140
+ ) -> None:
141
+ if topic:
142
+ key = _norm(topic)
143
+ ctx = click.get_current_context()
144
+ dir_to_use = _resolve_docs_dir(ctx)
145
+ fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
146
+ if key in fs:
147
+ typer.echo(fs[key].read_text(encoding="utf-8", errors="replace"))
148
+ raise typer.Exit(code=0)
149
+ pkg = _discover_pkg_topics()
150
+ if key in pkg:
151
+ typer.echo(pkg[key].read_text(encoding="utf-8", errors="replace"))
152
+ raise typer.Exit(code=0)
153
+ raise typer.BadParameter(f"Unknown topic: {topic}")
154
+
155
+ @docs_app.command("list", help="List available documentation topics")
156
+ def list_topics() -> None:
157
+ ctx = click.get_current_context()
158
+ dir_to_use = _resolve_docs_dir(ctx)
159
+ fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
160
+ pkg = _discover_pkg_topics()
161
+
162
+ def _print(name: str, path: Path) -> None:
163
+ try:
164
+ typer.echo(f"{name}\t{path}")
165
+ except Exception:
166
+ typer.echo(name)
167
+
168
+ for name, path in fs.items():
169
+ _print(name, path)
170
+ for name, path in pkg.items():
171
+ if name not in fs:
172
+ _print(name, path)
173
+
174
+ @docs_app.command("show", help="Show docs for a topic (alternative to dynamic subcommand)")
175
+ def show(topic: str) -> None:
176
+ key = _norm(topic)
177
+ ctx = click.get_current_context()
178
+ dir_to_use = _resolve_docs_dir(ctx)
179
+ fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
180
+ if key in fs:
181
+ typer.echo(fs[key].read_text(encoding="utf-8", errors="replace"))
182
+ return
183
+ pkg = _discover_pkg_topics()
184
+ if key in pkg:
185
+ typer.echo(pkg[key].read_text(encoding="utf-8", errors="replace"))
186
+ return
187
+ raise typer.BadParameter(f"Unknown topic: {topic}")
188
+
189
+ app.add_typer(docs_app, name="docs")
@@ -1,225 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- import os
5
- import sys
6
- from importlib.metadata import PackageNotFoundError, distribution
7
- from pathlib import Path
8
- from typing import Dict, List
9
-
10
- import click
11
- import typer
12
- from typer.core import TyperGroup
13
-
14
- from svc_infra.app.root import resolve_project_root
15
-
16
- # ---------- small helpers ----------
17
-
18
-
19
- def _norm(name: str) -> str:
20
- return name.strip().lower().replace(" ", "-").replace("_", "-")
21
-
22
-
23
- def _md_topics_in(dirpath: Path) -> Dict[str, Path]:
24
- topics: Dict[str, Path] = {}
25
- if dirpath.exists() and dirpath.is_dir():
26
- for p in sorted(dirpath.glob("*.md")):
27
- if p.is_file():
28
- topics[_norm(p.stem)] = p
29
- return topics
30
-
31
-
32
- # ---------- where could docs live after install? ----------
33
-
34
-
35
- def _candidate_docs_dirs(ctx: click.Context) -> List[Path]:
36
- """
37
- Return likely directories that contain the shipped docs (*.md), ordered by priority.
38
- Covers:
39
- 1) explicit --docs-dir / env var
40
- 2) in-repo (editable install): <repo>/docs relative to src/svc_infra
41
- 3) wheel installs: <site-packages>/docs
42
- 4) wheel .data area: <site-packages>/{name}-{ver}.data/**/docs
43
- """
44
- out: List[Path] = []
45
-
46
- # 1) explicit override (--docs-dir or env)
47
- # Walk up parent contexts to see Typer's group option.
48
- cur: click.Context | None = ctx
49
- while cur is not None:
50
- docs_dir_opt = (cur.params or {}).get("docs_dir")
51
- if docs_dir_opt:
52
- p = docs_dir_opt if isinstance(docs_dir_opt, Path) else Path(docs_dir_opt)
53
- p = p.expanduser()
54
- if p.exists():
55
- out.append(p)
56
- return out # explicit override wins
57
- cur = cur.parent
58
-
59
- env_dir = os.getenv("SVC_INFRA_DOCS_DIR")
60
- if env_dir:
61
- p = Path(env_dir).expanduser()
62
- if p.exists():
63
- out.append(p)
64
- return out # explicit override wins
65
-
66
- # locate installed package dir: .../site-packages/svc_infra
67
- pkg_dir: Path | None = None
68
- spec = importlib.util.find_spec("svc_infra")
69
- if spec and spec.submodule_search_locations:
70
- pkg_dir = Path(next(iter(spec.submodule_search_locations)))
71
-
72
- # 2) in-repo editable install: src/svc_infra -> ../../docs
73
- if pkg_dir:
74
- repo_root_docs = pkg_dir.parent.parent / "docs"
75
- if repo_root_docs.exists():
76
- out.append(repo_root_docs)
77
-
78
- # 3) wheel installs often end up with a top-level site-packages/docs
79
- top_level_docs = pkg_dir.parent / "docs"
80
- if top_level_docs.exists():
81
- out.append(top_level_docs)
82
-
83
- # 4) wheel .data layout: <site-packages>/{dist-name}-{version}.data/**/docs
84
- # This catches Poetry's include=docs/**/* paths installed by pip.
85
- # We compute sibling candidates off site-packages base.
86
- site_pkgs: Path | None = pkg_dir.parent if pkg_dir else None
87
- dist = None
88
- for name in ("svc-infra", "svc_infra"):
89
- try:
90
- dist = distribution(name)
91
- break
92
- except PackageNotFoundError:
93
- dist = None
94
-
95
- if site_pkgs and dist is not None:
96
- # normalized dist name (hyphen/underscore forms both happen in practice)
97
- dist_name = dist.metadata.get("Name", "svc-infra")
98
- dist_ver = dist.version
99
- data_candidates = [
100
- site_pkgs / f"{dist_name}-{dist_ver}.data",
101
- site_pkgs / f"{dist_name.replace('-', '_')}-{dist_ver}.data",
102
- site_pkgs / f"{dist_name.replace('_', '-')}-{dist_ver}.data",
103
- ]
104
- for data_dir in data_candidates:
105
- if not data_dir.exists():
106
- continue
107
- # common wheel data subfolders
108
- for sub in ("purelib", "platlib", "data"):
109
- d = data_dir / sub / "docs"
110
- if d.exists():
111
- out.append(d)
112
- # fallback: search shallowly for any docs/ folder inside .data
113
- for root, dirs, _files in os.walk(data_dir):
114
- root_path = Path(root)
115
- # limit depth (cheap)
116
- if len(root_path.parts) - len(data_dir.parts) > 3:
117
- dirs[:] = []
118
- continue
119
- if root_path.name == "docs":
120
- out.append(root_path)
121
-
122
- # 5) extremely defensive: scan sys.path entries that look like site-/dist-packages for top-level docs/
123
- for entry in sys.path:
124
- if not entry or ("site-packages" not in entry and "dist-packages" not in entry):
125
- continue
126
- p = Path(entry) / "docs"
127
- if p.exists():
128
- out.append(p)
129
-
130
- # de-dup while preserving order
131
- seen = set()
132
- uniq: List[Path] = []
133
- for p in out:
134
- if p.exists():
135
- key = str(p.resolve())
136
- if key not in seen:
137
- seen.add(key)
138
- uniq.append(p)
139
- return uniq
140
-
141
-
142
- def _discover_topics(ctx: click.Context) -> Dict[str, Path]:
143
- topics: Dict[str, Path] = {}
144
- for d in _candidate_docs_dirs(ctx):
145
- found = _md_topics_in(d)
146
- # do not override earlier (higher-priority) sources
147
- for k, v in found.items():
148
- topics.setdefault(k, v)
149
- if topics:
150
- # one dir with content is enough for most setups
151
- # (comment out this break if you *want* deep merging)
152
- break
153
- return topics
154
-
155
-
156
- # ---------- Typer group ----------
157
-
158
-
159
- class DocsGroup(TyperGroup):
160
- def list_commands(self, ctx: click.Context) -> List[str]:
161
- topics = _discover_topics(ctx)
162
- return sorted(topics.keys())
163
-
164
- def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
165
- cmd = super().get_command(ctx, name)
166
- if cmd is not None:
167
- return cmd
168
-
169
- key = _norm(name)
170
- topics = _discover_topics(ctx)
171
- if key in topics:
172
- file_path = topics[key]
173
-
174
- @click.command(name=name)
175
- def _show() -> None:
176
- click.echo(file_path.read_text(encoding="utf-8", errors="replace"))
177
-
178
- return _show
179
-
180
- return None
181
-
182
-
183
- def register(app: typer.Typer) -> None:
184
- docs_app = typer.Typer(no_args_is_help=True, add_completion=False, cls=DocsGroup)
185
-
186
- @docs_app.callback(invoke_without_command=True)
187
- def _docs_options(
188
- docs_dir: Path | None = typer.Option(
189
- None,
190
- "--docs-dir",
191
- help="Path to a docs directory to read from (overrides packaged docs)",
192
- ),
193
- topic: str | None = typer.Option(None, "--topic", help="Topic to show directly"),
194
- ) -> None:
195
- if topic:
196
- key = _norm(topic)
197
- topics = _discover_topics(click.get_current_context())
198
- if key in topics:
199
- typer.echo(topics[key].read_text(encoding="utf-8", errors="replace"))
200
- raise typer.Exit(code=0)
201
- raise typer.BadParameter(f"Unknown topic: {topic}")
202
-
203
- @docs_app.command("list", help="List available documentation topics")
204
- def list_topics() -> None:
205
- ctx = click.get_current_context()
206
- root = resolve_project_root()
207
- topics = _discover_topics(ctx)
208
-
209
- for name, path in topics.items():
210
- try:
211
- rel = path.relative_to(root)
212
- typer.echo(f"{name}\t{rel}")
213
- except Exception:
214
- typer.echo(f"{name}\t{path}")
215
-
216
- @docs_app.command("show", help="Show docs for a topic (alternative to dynamic subcommand)")
217
- def show(topic: str) -> None:
218
- key = _norm(topic)
219
- topics = _discover_topics(click.get_current_context())
220
- if key in topics:
221
- typer.echo(topics[key].read_text(encoding="utf-8", errors="replace"))
222
- return
223
- raise typer.BadParameter(f"Unknown topic: {topic}")
224
-
225
- app.add_typer(docs_app, name="docs")