pz-cli 5.0.0__py3-none-any.whl

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 (399) hide show
  1. pz_cli-5.0.0.dist-info/METADATA +51 -0
  2. pz_cli-5.0.0.dist-info/RECORD +399 -0
  3. pz_cli-5.0.0.dist-info/WHEEL +4 -0
  4. pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
  5. pz_core/__init__.py +64 -0
  6. pz_core/__main__.py +7 -0
  7. pz_core/app.py +45 -0
  8. pz_core/assembly/__init__.py +17 -0
  9. pz_core/assembly/app_discovery.py +1240 -0
  10. pz_core/assembly/apps/__init__.py +25 -0
  11. pz_core/assembly/apps/compiler.py +824 -0
  12. pz_core/assembly/apps/providers.py +188 -0
  13. pz_core/assembly/cli/__init__.py +9 -0
  14. pz_core/assembly/cli/compiler.py +166 -0
  15. pz_core/assembly/diagnostics/__init__.py +21 -0
  16. pz_core/assembly/diagnostics/checks.py +257 -0
  17. pz_core/assembly/diagnostics/dev_sources.py +569 -0
  18. pz_core/assembly/events/__init__.py +5 -0
  19. pz_core/assembly/events/compiler.py +219 -0
  20. pz_core/assembly/extensions/__init__.py +5 -0
  21. pz_core/assembly/extensions/compiler.py +696 -0
  22. pz_core/assembly/infra/__init__.py +6 -0
  23. pz_core/assembly/infra/compiler.py +272 -0
  24. pz_core/assembly/lifecycle/__init__.py +5 -0
  25. pz_core/assembly/lifecycle/compiler.py +95 -0
  26. pz_core/assembly/middlewares/__init__.py +19 -0
  27. pz_core/assembly/middlewares/compiler.py +508 -0
  28. pz_core/assembly/project/__init__.py +7 -0
  29. pz_core/assembly/project/compiler.py +9 -0
  30. pz_core/assembly/project/context.py +17 -0
  31. pz_core/assembly/project/discovery.py +227 -0
  32. pz_core/assembly/resources/__init__.py +19 -0
  33. pz_core/assembly/resources/compiler.py +310 -0
  34. pz_core/assembly/resources/settings.py +129 -0
  35. pz_core/assembly/routes/__init__.py +7 -0
  36. pz_core/assembly/routes/compiler.py +400 -0
  37. pz_core/assembly/runtime_manifests/__init__.py +13 -0
  38. pz_core/assembly/runtime_manifests/compiler.py +159 -0
  39. pz_core/assembly/settings/__init__.py +17 -0
  40. pz_core/assembly/settings/compiler.py +786 -0
  41. pz_core/assembly/settings/host_project_overrides.py +82 -0
  42. pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
  43. pz_core/authoring/__init__.py +79 -0
  44. pz_core/authoring/app_init_service.py +946 -0
  45. pz_core/authoring/app_versions.py +814 -0
  46. pz_core/authoring/cli.py +192 -0
  47. pz_core/authoring/commands.py +1810 -0
  48. pz_core/authoring/conflicts.py +180 -0
  49. pz_core/authoring/dev_bindings.py +916 -0
  50. pz_core/authoring/dev_git.py +1087 -0
  51. pz_core/authoring/dev_import.py +1061 -0
  52. pz_core/authoring/dev_manifest.py +522 -0
  53. pz_core/authoring/dev_sources.py +1638 -0
  54. pz_core/authoring/env_cli.py +454 -0
  55. pz_core/authoring/files.py +109 -0
  56. pz_core/authoring/ide_projection.py +1129 -0
  57. pz_core/authoring/naming.py +58 -0
  58. pz_core/authoring/official_dev_sources.py +432 -0
  59. pz_core/authoring/official_sources.json +156 -0
  60. pz_core/authoring/orchestration_service.py +558 -0
  61. pz_core/authoring/project_default.jsonc +53 -0
  62. pz_core/authoring/project_defaults.py +88 -0
  63. pz_core/authoring/project_metadata_service.py +199 -0
  64. pz_core/authoring/project_settings_checks.py +51 -0
  65. pz_core/authoring/project_settings_cli.py +299 -0
  66. pz_core/authoring/project_settings_service.py +1700 -0
  67. pz_core/authoring/project_state.py +399 -0
  68. pz_core/authoring/publish_git.py +423 -0
  69. pz_core/authoring/publish_lock.py +188 -0
  70. pz_core/authoring/publish_source.py +195 -0
  71. pz_core/authoring/release.py +1382 -0
  72. pz_core/authoring/repo_keys.py +29 -0
  73. pz_core/authoring/root_cli.py +28 -0
  74. pz_core/authoring/scaffold_service.py +325 -0
  75. pz_core/authoring/sync_service.py +787 -0
  76. pz_core/authoring/templates/app/__init__.py +1 -0
  77. pz_core/authoring/templates/app/api.py +25 -0
  78. pz_core/authoring/templates/app/apps.py +31 -0
  79. pz_core/authoring/templates/app/auth_package_init.py +14 -0
  80. pz_core/authoring/templates/app/auth_readme.py +29 -0
  81. pz_core/authoring/templates/app/configs_package_init.py +14 -0
  82. pz_core/authoring/templates/app/default_env.py +18 -0
  83. pz_core/authoring/templates/app/default_jsonc.py +25 -0
  84. pz_core/authoring/templates/app/locales_readme.py +25 -0
  85. pz_core/authoring/templates/app/migrations_readme.py +19 -0
  86. pz_core/authoring/templates/app/models.py +14 -0
  87. pz_core/authoring/templates/app/package_init.py +8 -0
  88. pz_core/authoring/templates/app/pages_base.py +161 -0
  89. pz_core/authoring/templates/app/pages_readme.py +25 -0
  90. pz_core/authoring/templates/app/public.py +31 -0
  91. pz_core/authoring/templates/app/pyproject.py +77 -0
  92. pz_core/authoring/templates/app/readme.py +74 -0
  93. pz_core/authoring/templates/app/settings.py +46 -0
  94. pz_core/authoring/templates/app/static_app_js.py +24 -0
  95. pz_core/authoring/templates/app/static_readme.py +31 -0
  96. pz_core/authoring/templates/app/template_config_page.py +23 -0
  97. pz_core/authoring/templates/app/template_index_html.py +28 -0
  98. pz_core/authoring/templates/app/templates_readme.py +32 -0
  99. pz_core/authoring/templates/app/urls.py +36 -0
  100. pz_core/authoring/templates/app/views.py +36 -0
  101. pz_core/authoring/templates/project/__init__.py +1 -0
  102. pz_core/authoring/templates/project/agents.py +51 -0
  103. pz_core/authoring/templates/project/apps_container_readme.py +37 -0
  104. pz_core/authoring/templates/project/main_py.py +59 -0
  105. pz_core/authoring/templates/project/project_env.py +19 -0
  106. pz_core/authoring/templates/project/pyproject.py +26 -0
  107. pz_core/authoring/templates/project/pz_env_env.py +65 -0
  108. pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
  109. pz_core/authoring/templates/project/pz_jsonc.py +12 -0
  110. pz_core/authoring/templates/project/readme.py +94 -0
  111. pz_core/authoring/templates/project/root_gitkeep.py +8 -0
  112. pz_core/authoring/templates/shared/__init__.py +5 -0
  113. pz_core/authoring/templates/shared/text.py +10 -0
  114. pz_core/authoring/venv_service.py +87 -0
  115. pz_core/authoring/workspace.py +167 -0
  116. pz_core/cli.py +291 -0
  117. pz_core/cli_options.py +74 -0
  118. pz_core/core/__init__.py +3 -0
  119. pz_core/core/errors/__init__.py +5 -0
  120. pz_core/core/errors/models.py +117 -0
  121. pz_core/core/events/__init__.py +5 -0
  122. pz_core/core/events/registry.py +54 -0
  123. pz_core/core/extensions/__init__.py +51 -0
  124. pz_core/core/extensions/registry.py +790 -0
  125. pz_core/core/health/__init__.py +6 -0
  126. pz_core/core/health/contracts.py +10 -0
  127. pz_core/core/middlewares/__init__.py +6 -0
  128. pz_core/core/middlewares/models.py +24 -0
  129. pz_core/core/routing/__init__.py +5 -0
  130. pz_core/core/routing/models.py +21 -0
  131. pz_core/core/settings_contracts.py +25 -0
  132. pz_core/docs/README.md +20 -0
  133. pz_core/docs/distribution-README.md +438 -0
  134. pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
  135. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
  136. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
  137. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
  138. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
  139. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
  140. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
  141. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
  142. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
  143. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
  144. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
  145. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
  146. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
  147. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
  148. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
  149. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
  150. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
  151. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
  152. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
  153. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
  154. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
  155. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
  156. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
  157. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
  158. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
  159. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
  160. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
  161. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
  162. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
  163. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
  164. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
  165. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
  166. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
  167. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
  168. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
  169. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
  170. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
  171. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
  172. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
  173. pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
  174. pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
  175. pz_core/infra/__init__.py +17 -0
  176. pz_core/infra/db/__init__.py +23 -0
  177. pz_core/infra/db/contracts.py +73 -0
  178. pz_core/infra/health/__init__.py +5 -0
  179. pz_core/infra/health/contracts.py +8 -0
  180. pz_core/infra/kv/__init__.py +5 -0
  181. pz_core/infra/kv/contracts.py +17 -0
  182. pz_core/infra/observability/__init__.py +23 -0
  183. pz_core/infra/observability/contracts.py +142 -0
  184. pz_core/infra/resilience/__init__.py +5 -0
  185. pz_core/infra/resilience/contracts.py +17 -0
  186. pz_core/infra/resources/__init__.py +5 -0
  187. pz_core/infra/resources/contracts.py +16 -0
  188. pz_core/infra/settings/__init__.py +17 -0
  189. pz_core/infra/settings/models.py +81 -0
  190. pz_core/infra/storage/__init__.py +5 -0
  191. pz_core/infra/storage/ports.py +27 -0
  192. pz_core/project_root_commands.py +282 -0
  193. pz_core/project_runtime_cli.py +344 -0
  194. pz_core/project_sources.py +253 -0
  195. pz_core/pz_launcher.py +209 -0
  196. pz_core/root_cli.py +70 -0
  197. pz_core/runtime/__init__.py +229 -0
  198. pz_core/runtime/access.py +86 -0
  199. pz_core/runtime/adapters/__init__.py +5 -0
  200. pz_core/runtime/adapters/base.py +18 -0
  201. pz_core/runtime/asyncio_bridge.py +18 -0
  202. pz_core/runtime/bindings/__init__.py +5 -0
  203. pz_core/runtime/bindings/container.py +50 -0
  204. pz_core/runtime/cli/__init__.py +5 -0
  205. pz_core/runtime/cli/_args.py +67 -0
  206. pz_core/runtime/cli/app_commands.py +417 -0
  207. pz_core/runtime/cli/check.py +214 -0
  208. pz_core/runtime/cli/db.py +297 -0
  209. pz_core/runtime/cli/main.py +89 -0
  210. pz_core/runtime/cli/resources.py +280 -0
  211. pz_core/runtime/discovery.py +23 -0
  212. pz_core/runtime/environment_integrity.py +157 -0
  213. pz_core/runtime/errors.py +5 -0
  214. pz_core/runtime/events.py +87 -0
  215. pz_core/runtime/facades/__init__.py +38 -0
  216. pz_core/runtime/facades/_capabilities.py +46 -0
  217. pz_core/runtime/facades/cache.py +103 -0
  218. pz_core/runtime/facades/database.py +71 -0
  219. pz_core/runtime/facades/events.py +380 -0
  220. pz_core/runtime/facades/health.py +101 -0
  221. pz_core/runtime/facades/mail.py +121 -0
  222. pz_core/runtime/facades/observability.py +102 -0
  223. pz_core/runtime/facades/providers.py +75 -0
  224. pz_core/runtime/facades/resources.py +52 -0
  225. pz_core/runtime/facades/storage.py +296 -0
  226. pz_core/runtime/health/__init__.py +5 -0
  227. pz_core/runtime/health/service.py +102 -0
  228. pz_core/runtime/host.py +188 -0
  229. pz_core/runtime/host_runner.py +282 -0
  230. pz_core/runtime/http/__init__.py +20 -0
  231. pz_core/runtime/http/app.py +576 -0
  232. pz_core/runtime/http_timing.py +173 -0
  233. pz_core/runtime/invocation.py +29 -0
  234. pz_core/runtime/json_safe.py +47 -0
  235. pz_core/runtime/lifecycle/__init__.py +25 -0
  236. pz_core/runtime/lifecycle/manager.py +533 -0
  237. pz_core/runtime/plan.py +214 -0
  238. pz_core/runtime/plan_context.py +33 -0
  239. pz_core/runtime/plan_snapshot.py +177 -0
  240. pz_core/runtime/registries/__init__.py +27 -0
  241. pz_core/runtime/registries/capabilities.py +101 -0
  242. pz_core/runtime/registries/providers.py +396 -0
  243. pz_core/runtime/resource_assets.py +344 -0
  244. pz_core/runtime/run_cli.py +211 -0
  245. pz_core/runtime/settings.py +888 -0
  246. pz_core/runtime/worker/__init__.py +21 -0
  247. pz_core/runtime/worker/engine.py +921 -0
  248. pz_core/runtime/worker/runtime.py +25 -0
  249. pz_core/testing/__init__.py +15 -0
  250. pz_core/testing/doc_route_contracts.py +305 -0
  251. pz_core/utils/__init__.py +44 -0
  252. pz_core/utils/env.py +65 -0
  253. pz_core/utils/project_jsonc/__init__.py +29 -0
  254. pz_core/utils/project_jsonc/document.py +420 -0
  255. pz_core/utils/project_settings.py +99 -0
  256. pz_foundation/__init__.py +50 -0
  257. pz_foundation/_contracts.py +81 -0
  258. pz_foundation/_errors.py +34 -0
  259. pz_foundation/api.py +14 -0
  260. pz_foundation/apps.py +127 -0
  261. pz_foundation/auth/__init__.py +1 -0
  262. pz_foundation/cache_control.py +233 -0
  263. pz_foundation/cli/__init__.py +2 -0
  264. pz_foundation/cli/workers.py +565 -0
  265. pz_foundation/configs/__init__.py +0 -0
  266. pz_foundation/configs/default.env +15 -0
  267. pz_foundation/configs/default.jsonc +452 -0
  268. pz_foundation/db/__init__.py +94 -0
  269. pz_foundation/db/aerich_executor.py +664 -0
  270. pz_foundation/db/config.py +358 -0
  271. pz_foundation/db/connection_manager.py +689 -0
  272. pz_foundation/db/connection_probe.py +204 -0
  273. pz_foundation/db/database.py +1291 -0
  274. pz_foundation/db/deps.py +47 -0
  275. pz_foundation/db/lifecycle_status.py +85 -0
  276. pz_foundation/db/migrations.py +477 -0
  277. pz_foundation/db/mysql.py +57 -0
  278. pz_foundation/db/postgresql.py +409 -0
  279. pz_foundation/db/production_pool_fallback.py +143 -0
  280. pz_foundation/db/public.py +45 -0
  281. pz_foundation/db/session.py +369 -0
  282. pz_foundation/db/sqlite.py +15 -0
  283. pz_foundation/db/tortoise_manager.py +724 -0
  284. pz_foundation/db/tortoise_psycopg.py +148 -0
  285. pz_foundation/docs/README.md +18 -0
  286. pz_foundation/docs/distribution-README.md +319 -0
  287. pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
  288. pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
  289. pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
  290. pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
  291. pz_foundation/health.py +105 -0
  292. pz_foundation/host/__init__.py +11 -0
  293. pz_foundation/host/builder.py +34 -0
  294. pz_foundation/host/cache_runtime.py +44 -0
  295. pz_foundation/host/db/__init__.py +17 -0
  296. pz_foundation/host/db/aerich_config.py +36 -0
  297. pz_foundation/host/db/config.py +106 -0
  298. pz_foundation/host/db/connection_manager.py +48 -0
  299. pz_foundation/host/db/deps.py +5 -0
  300. pz_foundation/host/db/orm_retry_patch.py +566 -0
  301. pz_foundation/host/db/request_adapter.py +325 -0
  302. pz_foundation/host/db/router.py +28 -0
  303. pz_foundation/host/db/session.py +284 -0
  304. pz_foundation/host/db/tortoise_manager.py +10 -0
  305. pz_foundation/host/idempotency.py +132 -0
  306. pz_foundation/host/lifespan.py +247 -0
  307. pz_foundation/host/redis_runtime.py +100 -0
  308. pz_foundation/host/web.py +790 -0
  309. pz_foundation/infra_provider.py +236 -0
  310. pz_foundation/kv/__init__.py +25 -0
  311. pz_foundation/kv/cache.py +1166 -0
  312. pz_foundation/kv/deps.py +84 -0
  313. pz_foundation/kv/public.py +25 -0
  314. pz_foundation/kv/redis_manager.py +1531 -0
  315. pz_foundation/lazy_exports.py +43 -0
  316. pz_foundation/lazy_imports.py +124 -0
  317. pz_foundation/lifecycle.py +392 -0
  318. pz_foundation/managed_workers.py +1117 -0
  319. pz_foundation/middlewares/__init__.py +26 -0
  320. pz_foundation/middlewares/_settings.py +250 -0
  321. pz_foundation/middlewares/analytics.py +124 -0
  322. pz_foundation/middlewares/cache_control.py +156 -0
  323. pz_foundation/middlewares/compression.py +202 -0
  324. pz_foundation/middlewares/cors.py +62 -0
  325. pz_foundation/middlewares/geo_country.py +44 -0
  326. pz_foundation/middlewares/inner_router.py +50 -0
  327. pz_foundation/middlewares/rate_limited/__init__.py +3 -0
  328. pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
  329. pz_foundation/middlewares/request_logging.py +104 -0
  330. pz_foundation/middlewares/route_owner.py +309 -0
  331. pz_foundation/middlewares/security_csrf.py +44 -0
  332. pz_foundation/middlewares/security_headers/__init__.py +4 -0
  333. pz_foundation/middlewares/security_headers/conflicts.py +44 -0
  334. pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
  335. pz_foundation/middlewares/security_headers/security_headers.py +44 -0
  336. pz_foundation/migrations/__init__.py +1 -0
  337. pz_foundation/migrations/pz_foundation/__init__.py +1 -0
  338. pz_foundation/models.py +22 -0
  339. pz_foundation/notifications/__init__.py +23 -0
  340. pz_foundation/notifications/email.py +542 -0
  341. pz_foundation/notifications/public.py +31 -0
  342. pz_foundation/observability/__init__.py +71 -0
  343. pz_foundation/observability/http.py +34 -0
  344. pz_foundation/observability/logger/__init__.py +49 -0
  345. pz_foundation/observability/logger/_bot_settings.py +126 -0
  346. pz_foundation/observability/logger/bot_message.py +592 -0
  347. pz_foundation/observability/logger/core.py +385 -0
  348. pz_foundation/observability/logger/delivery_audit.py +84 -0
  349. pz_foundation/observability/logger/feishu_bot.py +199 -0
  350. pz_foundation/observability/logger/telegram_bot.py +194 -0
  351. pz_foundation/observability/metrics.py +365 -0
  352. pz_foundation/observability/runtime.py +68 -0
  353. pz_foundation/public.py +405 -0
  354. pz_foundation/resources/__init__.py +12 -0
  355. pz_foundation/resources/builder.py +900 -0
  356. pz_foundation/resources/images.py +363 -0
  357. pz_foundation/root_cli.py +20 -0
  358. pz_foundation/routing/__init__.py +203 -0
  359. pz_foundation/routing/canonical.py +486 -0
  360. pz_foundation/routing/convertors.py +82 -0
  361. pz_foundation/routing/declarations.py +843 -0
  362. pz_foundation/routing/exports.py +148 -0
  363. pz_foundation/routing/loaders.py +138 -0
  364. pz_foundation/routing/manifest.py +215 -0
  365. pz_foundation/routing/manifest_provider.py +47 -0
  366. pz_foundation/routing/mounts.py +101 -0
  367. pz_foundation/routing/ordering.py +172 -0
  368. pz_foundation/routing/registry.py +133 -0
  369. pz_foundation/routing/utils.py +28 -0
  370. pz_foundation/runtime.py +234 -0
  371. pz_foundation/settings.py +253 -0
  372. pz_foundation/settings_normalization.py +130 -0
  373. pz_foundation/storage/__init__.py +25 -0
  374. pz_foundation/storage/local.py +209 -0
  375. pz_foundation/storage/public.py +106 -0
  376. pz_foundation/storage/s3.py +116 -0
  377. pz_foundation/storage/service.py +234 -0
  378. pz_foundation/urls.py +19 -0
  379. pz_foundation/utils/__init__.py +6 -0
  380. pz_foundation/utils/merge.py +160 -0
  381. pz_foundation/worker_replicas.py +55 -0
  382. pz_settings_center/__init__.py +5 -0
  383. pz_settings_center/api/__init__.py +5 -0
  384. pz_settings_center/api/runtime_overrides.py +360 -0
  385. pz_settings_center/apps.py +28 -0
  386. pz_settings_center/configs/__init__.py +0 -0
  387. pz_settings_center/configs/default.env +2 -0
  388. pz_settings_center/configs/default.jsonc +1 -0
  389. pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
  390. pz_settings_center/docs/README.md +12 -0
  391. pz_settings_center/docs/distribution-README.md +150 -0
  392. pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
  393. pz_settings_center/integration_payload.py +18 -0
  394. pz_settings_center/project_helpers.py +405 -0
  395. pz_settings_center/public.py +139 -0
  396. pz_settings_center/routing_helpers.py +73 -0
  397. pz_settings_center/runtime_override_provider.py +61 -0
  398. pz_settings_center/typed_settings.py +324 -0
  399. pz_settings_center/urls.py +9 -0
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from pz_core.runtime.errors import PzError
6
+ from pz_core.runtime.registries.providers import (
7
+ ProviderBinding,
8
+ ProviderActivationContext,
9
+ ProviderDecl,
10
+ ProviderDiagnostic,
11
+ ProviderRegistry,
12
+ )
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class ProviderFacade:
17
+ registry: ProviderRegistry
18
+
19
+ def __post_init__(self) -> None:
20
+ if not isinstance(self.registry, ProviderRegistry):
21
+ raise PzError(
22
+ code="PZ_PROVIDER_REGISTRY_INVALID",
23
+ message="ProviderFacade requires a ProviderRegistry instance.",
24
+ stage="providers",
25
+ owner="runtime.providers",
26
+ action="bind_provider_facade",
27
+ )
28
+
29
+ def declared(self) -> tuple[ProviderDecl, ...]:
30
+ return self.registry.declared()
31
+
32
+ def declared_for(self, capability: str) -> tuple[ProviderDecl, ...]:
33
+ return self.registry.declared_for(capability)
34
+
35
+ def bindings(self) -> tuple[ProviderBinding, ...]:
36
+ return self.registry.bindings()
37
+
38
+ def bindings_for(self, capability: str) -> tuple[ProviderBinding, ...]:
39
+ return self.registry.bindings_for(capability)
40
+
41
+ def register(self, binding_id: str, provider: object) -> str:
42
+ return self.registry.register(binding_id, provider)
43
+
44
+ def unregister(self, token: str) -> bool:
45
+ return self.registry.unregister(token)
46
+
47
+ def reset(self, *, owner: str | None = None) -> int:
48
+ return self.registry.reset(owner=owner)
49
+
50
+ def activate_declared(
51
+ self,
52
+ *,
53
+ plan: object,
54
+ bindings: object,
55
+ host: object | None = None,
56
+ owner: str | None = None,
57
+ ) -> int:
58
+ return self.registry.activate_declared(
59
+ plan=plan,
60
+ bindings=bindings,
61
+ host=host,
62
+ owner=owner,
63
+ )
64
+
65
+ def require(self, capability: str) -> object:
66
+ return self.registry.require(capability)
67
+
68
+ def status(self, capability: str) -> str:
69
+ return self.registry.status(capability)
70
+
71
+ def failed_for(self, capability: str) -> tuple[ProviderDiagnostic, ...]:
72
+ return self.registry.failed_for(capability)
73
+
74
+ def diagnostics(self) -> tuple[ProviderDiagnostic, ...]:
75
+ return self.registry.diagnostics()
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ from pz_core.assembly.resources import ResourceManifest, ResourceSource
4
+ from pz_core.infra.resources import ResourceBuildPort, ResourceBuildResult
5
+ from pz_core.runtime.errors import PzError
6
+ from pz_core.runtime.facades.providers import ProviderFacade
7
+
8
+
9
+ class ResourceFacade:
10
+ def __init__(self, manifest: ResourceManifest, providers: ProviderFacade) -> None:
11
+ if not isinstance(manifest, ResourceManifest):
12
+ raise PzError(
13
+ code="PZ_RESOURCE_MANIFEST_INVALID",
14
+ message="ResourceFacade requires a ResourceManifest instance.",
15
+ stage="resources",
16
+ owner="runtime.resources",
17
+ action="bind_resource_facade",
18
+ )
19
+ if not hasattr(providers, "require"):
20
+ raise PzError(
21
+ code="PZ_RESOURCE_PROVIDERS_INVALID",
22
+ message="ResourceFacade providers must expose require().",
23
+ stage="resources",
24
+ owner="runtime.resources",
25
+ action="bind_resource_facade",
26
+ )
27
+ self._manifest = manifest
28
+ self._providers = providers
29
+
30
+ def status(self) -> str:
31
+ return self._providers.status("resources")
32
+
33
+ def manifest(self) -> ResourceManifest:
34
+ return self._manifest
35
+
36
+ def app_order(self) -> tuple[str, ...]:
37
+ return self._manifest.app_order
38
+
39
+ def sources(self) -> tuple[ResourceSource, ...]:
40
+ return self._manifest.sources
41
+
42
+ def build(self, *, force: bool = False) -> ResourceBuildResult:
43
+ provider = self._providers.require("resources")
44
+ if not isinstance(provider, ResourceBuildPort):
45
+ raise PzError(
46
+ code="PZ_RESOURCE_PROVIDER_INVALID",
47
+ message="Registered resources provider does not implement ResourceBuildPort.",
48
+ stage="resources",
49
+ owner="runtime.resources",
50
+ action="build",
51
+ )
52
+ return provider.build(force=force)
@@ -0,0 +1,296 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Protocol, runtime_checkable
5
+
6
+ from pz_core.runtime.errors import PzError
7
+ from pz_core.infra.storage import PresignInfo, StoragePort, StoredObject
8
+ from pz_core.runtime.facades._capabilities import merged_capability_status
9
+ from pz_core.runtime.access import get_runtime
10
+ from pz_core.runtime.facades.providers import ProviderFacade
11
+ from pz_core.runtime.plan import RuntimePlan
12
+ from pz_core.runtime.registries import CapabilityStatus
13
+
14
+
15
+ def _require_storage_key(key: str, *, action: str) -> str:
16
+ if not isinstance(key, str) or not key.strip():
17
+ raise PzError(
18
+ code="PZ_STORAGE_KEY_INVALID",
19
+ message="Storage key must be a non-empty string.",
20
+ stage="storage",
21
+ owner="runtime.storage",
22
+ action=action,
23
+ )
24
+ return key
25
+
26
+
27
+ def _require_storage_mode(mode: str, *, action: str) -> str:
28
+ if not isinstance(mode, str) or not mode.strip():
29
+ raise PzError(
30
+ code="PZ_STORAGE_MODE_INVALID",
31
+ message="Storage mode must be a non-empty string.",
32
+ stage="storage",
33
+ owner="runtime.storage",
34
+ action=action,
35
+ )
36
+ return mode
37
+
38
+
39
+ def _require_storage_method(method: str, *, action: str) -> str:
40
+ if not isinstance(method, str) or not method.strip():
41
+ raise PzError(
42
+ code="PZ_STORAGE_METHOD_INVALID",
43
+ message="Storage method must be a non-empty string.",
44
+ stage="storage",
45
+ owner="runtime.storage",
46
+ action=action,
47
+ )
48
+ return method
49
+
50
+
51
+ def _require_storage_expires_in(expires_in: int | None, *, action: str) -> int | None:
52
+ if expires_in is None:
53
+ return None
54
+ if isinstance(expires_in, bool) or not isinstance(expires_in, int) or expires_in <= 0:
55
+ raise PzError(
56
+ code="PZ_STORAGE_EXPIRES_IN_INVALID",
57
+ message="Storage expires_in must be a positive integer when configured.",
58
+ stage="storage",
59
+ owner="runtime.storage",
60
+ action=action,
61
+ )
62
+ return expires_in
63
+
64
+
65
+ class StorageFacade:
66
+ def __init__(self, plan: RuntimePlan, providers: ProviderFacade) -> None:
67
+ if not isinstance(plan, RuntimePlan):
68
+ raise PzError(
69
+ code="PZ_STORAGE_PLAN_INVALID",
70
+ message="StorageFacade requires a RuntimePlan instance.",
71
+ stage="storage",
72
+ owner="runtime.storage",
73
+ action="bind_storage_facade",
74
+ )
75
+ if not hasattr(providers, "require"):
76
+ raise PzError(
77
+ code="PZ_STORAGE_PROVIDERS_INVALID",
78
+ message="StorageFacade providers must expose require().",
79
+ stage="storage",
80
+ owner="runtime.storage",
81
+ action="bind_storage_facade",
82
+ )
83
+ self._plan = plan
84
+ self._providers = providers
85
+
86
+ def status(self) -> CapabilityStatus:
87
+ return merged_capability_status(self._plan, self._providers, "storage")
88
+
89
+ def _storage(self) -> StoragePort:
90
+ provider = self._providers.require("storage")
91
+ if not isinstance(provider, StoragePort):
92
+ raise PzError(
93
+ code="PZ_STORAGE_PROVIDER_INVALID",
94
+ message="Registered storage provider does not implement StoragePort.",
95
+ stage="storage",
96
+ owner="infra.storage",
97
+ action="resolve_storage",
98
+ )
99
+ return provider
100
+
101
+ def open(self, key: str, *, mode: str = "rb"):
102
+ key = _require_storage_key(key, action="open")
103
+ mode = _require_storage_mode(mode, action="open")
104
+ provider = self._providers.require("storage")
105
+ opener = getattr(provider, "open", None)
106
+ if callable(opener):
107
+ return opener(key, mode=mode)
108
+ raise PzError(
109
+ code="PZ_STORAGE_OPEN_UNSUPPORTED",
110
+ message="Registered storage provider does not expose open().",
111
+ stage="storage",
112
+ owner="infra.storage",
113
+ action="open",
114
+ )
115
+
116
+ def save(
117
+ self,
118
+ key: str,
119
+ payload: object,
120
+ *,
121
+ content_type: str | None = None,
122
+ metadata: dict[str, str] | None = None,
123
+ ) -> StoredObject:
124
+ key = _require_storage_key(key, action="save")
125
+ del metadata
126
+ if isinstance(payload, bytes):
127
+ body = payload
128
+ elif isinstance(payload, str):
129
+ body = payload.encode("utf-8")
130
+ elif hasattr(payload, "read"):
131
+ body = payload.read()
132
+ if isinstance(body, str):
133
+ body = body.encode("utf-8")
134
+ else:
135
+ raise PzError(
136
+ code="PZ_STORAGE_PAYLOAD_INVALID",
137
+ message="StorageFacade.save() payload must be bytes, str, or file-like.",
138
+ stage="storage",
139
+ owner="infra.storage",
140
+ action="save",
141
+ )
142
+ if not isinstance(body, bytes):
143
+ raise PzError(
144
+ code="PZ_STORAGE_PAYLOAD_INVALID",
145
+ message="StorageFacade.save() payload reader must return bytes or str.",
146
+ stage="storage",
147
+ owner="infra.storage",
148
+ action="save",
149
+ )
150
+ return self._storage().store_bytes(key, body, content_type=content_type)
151
+
152
+ def delete(self, key: str) -> None:
153
+ key = _require_storage_key(key, action="delete")
154
+ self._storage().delete(key)
155
+
156
+ def url(
157
+ self,
158
+ key: str,
159
+ *,
160
+ expires_in: int | None = None,
161
+ download_name: str | None = None,
162
+ ) -> str | None:
163
+ key = _require_storage_key(key, action="url")
164
+ expires_in = _require_storage_expires_in(expires_in, action="url")
165
+ del download_name
166
+ provider = self._providers.require("storage")
167
+ url_factory = getattr(provider, "url", None)
168
+ if callable(url_factory):
169
+ return url_factory(key, expires_in=expires_in)
170
+ return None
171
+
172
+ def presign(
173
+ self,
174
+ key: str,
175
+ *,
176
+ method: str = "GET",
177
+ expires_in: int | None = None,
178
+ ) -> PresignInfo:
179
+ key = _require_storage_key(key, action="presign")
180
+ method = _require_storage_method(method, action="presign")
181
+ expires_in = _require_storage_expires_in(expires_in, action="presign")
182
+ provider = self._providers.require("storage")
183
+ presign_factory = getattr(provider, "presign", None)
184
+ if callable(presign_factory):
185
+ return presign_factory(key, method=method, expires_in=expires_in)
186
+ if method.upper() == "GET":
187
+ return self._storage().presign_get(
188
+ key,
189
+ expires_in_seconds=expires_in if expires_in is not None else 300,
190
+ )
191
+ raise PzError(
192
+ code="PZ_STORAGE_PRESIGN_UNSUPPORTED",
193
+ message="Registered storage provider does not expose presign() for this method.",
194
+ stage="storage",
195
+ owner="infra.storage",
196
+ action="presign",
197
+ )
198
+
199
+
200
+ @dataclass(slots=True)
201
+ class RuntimePresignInfo:
202
+ method: str
203
+ url: str
204
+ expires: int | None = None
205
+ headers: dict[str, str] | None = None
206
+ fields: dict[str, str] | None = None
207
+
208
+
209
+ @dataclass(slots=True)
210
+ class RuntimeStoredObject:
211
+ path: str
212
+ size: int | None = None
213
+ content_type: str | None = None
214
+
215
+
216
+ @runtime_checkable
217
+ class RuntimeStoragePort(Protocol):
218
+ def open(self, key: str, *, mode: str = "rb"): ...
219
+
220
+ def save(self, key: str, payload: Any, *, content_type: str | None = None) -> RuntimeStoredObject: ...
221
+
222
+ def delete(self, key: str) -> None: ...
223
+
224
+ def url(self, key: str, *, expires_in: int | None = None) -> str | None: ...
225
+
226
+ def presign_upload(
227
+ self,
228
+ key: str,
229
+ *,
230
+ content_type: str | None = None,
231
+ expires_in: int = 300,
232
+ ) -> RuntimePresignInfo: ...
233
+
234
+ def presign_download(self, key: str, *, expires_in: int = 300) -> RuntimePresignInfo: ...
235
+
236
+ class StorageClient:
237
+ def __init__(self, namespace: str | None = None, *, request: Any | None = None) -> None:
238
+ self.namespace = namespace
239
+ self._storage = get_runtime(request=request).bindings.storage
240
+
241
+ def open(self, key: str, *, mode: str = "rb"):
242
+ return self._storage.open(key, mode=mode)
243
+
244
+ def save(self, key: str, payload: Any, *, content_type: str | None = None) -> RuntimeStoredObject:
245
+ stored = self._storage.save(key, payload, content_type=content_type)
246
+ object_key = getattr(stored, "object_key", key)
247
+ return RuntimeStoredObject(
248
+ path=str(object_key),
249
+ size=getattr(stored, "size", None),
250
+ content_type=getattr(stored, "content_type", content_type),
251
+ )
252
+
253
+ def delete(self, key: str) -> None:
254
+ self._storage.delete(key)
255
+
256
+ def url(self, key: str, *, expires_in: int | None = None) -> str | None:
257
+ return self._storage.url(key, expires_in=expires_in)
258
+
259
+ def presign_upload(
260
+ self,
261
+ key: str,
262
+ *,
263
+ content_type: str | None = None,
264
+ expires_in: int = 300,
265
+ ) -> RuntimePresignInfo:
266
+ del content_type
267
+ presign = self._storage.presign(key, method="PUT", expires_in=expires_in)
268
+ return RuntimePresignInfo(
269
+ method=presign.method,
270
+ url=presign.url,
271
+ expires=presign.expires_in_seconds,
272
+ headers={},
273
+ fields={},
274
+ )
275
+
276
+ def presign_download(self, key: str, *, expires_in: int = 300) -> RuntimePresignInfo:
277
+ presign = self._storage.presign(key, method="GET", expires_in=expires_in)
278
+ return RuntimePresignInfo(
279
+ method=presign.method,
280
+ url=presign.url,
281
+ expires=presign.expires_in_seconds,
282
+ headers={},
283
+ fields={},
284
+ )
285
+
286
+
287
+ def get_storage(namespace: str | None = None):
288
+ if namespace is None:
289
+ def _dependency(request: Any) -> StorageClient:
290
+ return StorageClient(request=request)
291
+ return _dependency
292
+
293
+ def _dependency(request: Any) -> StorageClient:
294
+ return StorageClient(namespace=namespace, request=request)
295
+
296
+ return _dependency
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from .service import HealthSnapshot, build_health_snapshot
4
+
5
+ __all__ = ["HealthSnapshot", "build_health_snapshot"]
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Mapping
5
+
6
+ from pz_core.runtime.lifecycle import LifecycleState
7
+ from pz_core.runtime.plan import RuntimePlan
8
+ from pz_core.runtime.registries import CapabilityState, CapabilityStatus
9
+ from pz_core.runtime.errors import PzError
10
+
11
+
12
+ def _capability_map(
13
+ plan: RuntimePlan,
14
+ state: LifecycleState | None,
15
+ ) -> tuple[CapabilityState, ...]:
16
+ if state and state.capability_states:
17
+ return state.capability_states
18
+ return plan.capability_registry.capabilities
19
+
20
+
21
+ def _aggregate_status(statuses: tuple[CapabilityStatus, ...], *, active: bool) -> str:
22
+ if any(status == "unavailable" for status in statuses):
23
+ return "unavailable"
24
+ if active and any(status == "ready" for status in statuses):
25
+ return "ok"
26
+ if any(status == "enabled" for status in statuses):
27
+ return "starting"
28
+ if any(status == "ready" for status in statuses):
29
+ return "ok"
30
+ return "disabled"
31
+
32
+
33
+ def _normalize_extra_capability_statuses(
34
+ extra_capability_statuses: Mapping[str, CapabilityStatus] | None,
35
+ ) -> dict[str, CapabilityStatus]:
36
+ if not extra_capability_statuses:
37
+ return {}
38
+ normalized: dict[str, CapabilityStatus] = {}
39
+ for name, status in extra_capability_statuses.items():
40
+ if not isinstance(name, str) or not name.strip():
41
+ raise PzError(
42
+ code="PZ_CAPABILITY_NAME_INVALID",
43
+ message="extra capability status names must be non-empty strings.",
44
+ stage="health",
45
+ owner="runtime.health",
46
+ action="build_health_snapshot",
47
+ )
48
+ if status not in {"disabled", "enabled", "ready", "unavailable"}:
49
+ raise PzError(
50
+ code="PZ_CAPABILITY_STATUS_INVALID",
51
+ message=(
52
+ f"extra capability status for '{name.strip()}' must be one of: "
53
+ "disabled, enabled, ready, unavailable."
54
+ ),
55
+ stage="health",
56
+ owner=name.strip(),
57
+ action="build_health_snapshot",
58
+ )
59
+ normalized[name.strip()] = status
60
+ return normalized
61
+
62
+
63
+ @dataclass(frozen=True, slots=True)
64
+ class HealthSnapshot:
65
+ status: str
66
+ phase: str
67
+ active: bool
68
+ enabled_components: tuple[str, ...]
69
+ activated_capabilities: tuple[str, ...]
70
+ capability_statuses: dict[str, CapabilityStatus]
71
+ unavailable_capabilities: tuple[str, ...]
72
+
73
+
74
+ def build_health_snapshot(
75
+ plan: RuntimePlan,
76
+ state: LifecycleState | None = None,
77
+ *,
78
+ extra_capability_statuses: Mapping[str, CapabilityStatus] | None = None,
79
+ ) -> HealthSnapshot:
80
+ lifecycle = state or LifecycleState()
81
+ capabilities = _capability_map(plan, lifecycle if state else None)
82
+ capability_statuses = {capability.name: capability.status for capability in capabilities}
83
+ normalized_extra = _normalize_extra_capability_statuses(extra_capability_statuses)
84
+ if normalized_extra:
85
+ capability_statuses.update(normalized_extra)
86
+ statuses = tuple(capability.status for capability in capabilities)
87
+ if normalized_extra:
88
+ statuses += tuple(normalized_extra.values())
89
+ unavailable_capabilities = tuple(
90
+ name
91
+ for name, status in capability_statuses.items()
92
+ if status == "unavailable"
93
+ )
94
+ return HealthSnapshot(
95
+ status=_aggregate_status(statuses, active=lifecycle.active),
96
+ phase=lifecycle.phase,
97
+ active=lifecycle.active,
98
+ enabled_components=plan.infra.enabled_components,
99
+ activated_capabilities=lifecycle.activated_capabilities,
100
+ capability_statuses=capability_statuses,
101
+ unavailable_capabilities=unavailable_capabilities,
102
+ )