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,696 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from importlib import import_module
5
+ import inspect
6
+ from typing import Any
7
+
8
+ from pz_core.assembly.apps import AppRegistry, load_app_object_from_decl
9
+ from pz_core.assembly.project import active_project_import_root
10
+ from pz_core.assembly.settings import SettingsSnapshot
11
+ from pz_core.core.errors import PzError
12
+ from pz_core.core.extensions import (
13
+ CliToolSpec,
14
+ ExtensionRegistry,
15
+ ExtensionSpec,
16
+ FormalExtensionSpec,
17
+ HostBuilderSpec,
18
+ InfraProviderSpec,
19
+ LifecycleContributionSpec,
20
+ PageProfileProviderSpec,
21
+ ResourceContributionSpec,
22
+ RuntimeManifestContributionSpec,
23
+ ProtocolExtensionSpec,
24
+ ProtocolRegistrationSpec,
25
+ SettingsProviderSpec,
26
+ WorkerRoleSpec,
27
+ )
28
+
29
+
30
+ _MISSING = object()
31
+
32
+
33
+ def _payload_field(payload: Any, name: str, *, default: Any = None) -> Any:
34
+ if isinstance(payload, Mapping):
35
+ return payload.get(name, default)
36
+ value = getattr(payload, name, _MISSING)
37
+ if value is _MISSING:
38
+ return default
39
+ return value
40
+
41
+
42
+ def _resolve_dotted(target: str) -> Any:
43
+ module_name, attr_name = str(target or "").rsplit(".", 1)
44
+ module = import_module(module_name)
45
+ return getattr(module, attr_name)
46
+
47
+
48
+ def _instantiate_page_profile_provider(provider: Any) -> Any:
49
+ if isinstance(provider, str):
50
+ provider = _resolve_dotted(provider)
51
+ if inspect.isclass(provider):
52
+ return provider()
53
+ return provider
54
+
55
+
56
+ def _validate_page_profile_provider_spec(spec: PageProfileProviderSpec) -> PageProfileProviderSpec:
57
+ try:
58
+ provider = _instantiate_page_profile_provider(spec.provider)
59
+ except PzError:
60
+ raise
61
+ except Exception as exc:
62
+ raise PzError(
63
+ code="PZ_PAGE_PROFILE_PROVIDER_IMPORT_FAILED",
64
+ message=f"Page-profile provider '{spec.id}' failed to load during extension compilation: {exc}",
65
+ stage="extensions",
66
+ owner=spec.owner_app,
67
+ action="compile_extension_registry",
68
+ ) from exc
69
+ resolver = getattr(provider, "resolve_page_profile", None)
70
+ if not callable(resolver):
71
+ raise PzError(
72
+ code="PZ_PAGE_PROFILE_PROVIDER_INVALID",
73
+ message=f"Page-profile provider '{spec.id}' must expose resolve_page_profile(...).",
74
+ stage="extensions",
75
+ owner=spec.owner_app,
76
+ action="compile_extension_registry",
77
+ )
78
+ return spec
79
+
80
+
81
+ def _lifecycle_handlers_match(left: Any, right: Any) -> bool:
82
+ if left is right:
83
+ return True
84
+ left_func = getattr(left, "__func__", None)
85
+ right_func = getattr(right, "__func__", None)
86
+ if left_func is None or right_func is None:
87
+ return False
88
+ return left_func is right_func and getattr(left, "__self__", None) is getattr(right, "__self__", None)
89
+
90
+
91
+ def _declares_matching_lifecycle_hook(
92
+ declared: tuple[FormalExtensionSpec, ...],
93
+ *,
94
+ owner_app: str,
95
+ phase: str,
96
+ handler: Any,
97
+ ) -> bool:
98
+ for item in declared:
99
+ if not isinstance(item, LifecycleContributionSpec):
100
+ continue
101
+ if item.owner_app != owner_app or item.phase != phase:
102
+ continue
103
+ if _lifecycle_handlers_match(item.handler, handler):
104
+ return True
105
+ return False
106
+
107
+
108
+ def _synthesized_lifecycle_contributions(
109
+ app_decl: Any,
110
+ app_obj: Any,
111
+ *,
112
+ declared: tuple[FormalExtensionSpec, ...],
113
+ ) -> tuple[LifecycleContributionSpec, ...]:
114
+ out: list[LifecycleContributionSpec] = []
115
+ for phase in ("startup", "shutdown"):
116
+ hook = getattr(app_obj, phase, None)
117
+ if hook is None:
118
+ continue
119
+ if not callable(hook):
120
+ raise PzError(
121
+ code="PZ_APP_LIFECYCLE_HOOK_INVALID",
122
+ message=f"App '{app_decl.app_id}' exposes non-callable hook '{phase}'.",
123
+ stage="extensions",
124
+ owner=app_decl.app_id,
125
+ action="compile_extension_registry",
126
+ )
127
+ if _declares_matching_lifecycle_hook(
128
+ declared,
129
+ owner_app=app_decl.app_id,
130
+ phase=phase,
131
+ handler=hook,
132
+ ):
133
+ continue
134
+ out.append(
135
+ LifecycleContributionSpec(
136
+ id=f"{app_decl.app_id}.app_hook.{phase}",
137
+ owner_app=app_decl.app_id,
138
+ phase=phase,
139
+ handler=hook,
140
+ )
141
+ )
142
+ return tuple(out)
143
+
144
+
145
+ def _coerce_extension_spec(payload: Any, *, owner_hint: str) -> ExtensionSpec | None:
146
+ raw_kind = _payload_field(payload, "kind")
147
+ raw_id = _payload_field(payload, "id")
148
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
149
+ if not isinstance(raw_kind, str) or not raw_kind.strip():
150
+ return None
151
+ if not isinstance(raw_id, str) or not raw_id.strip():
152
+ return None
153
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
154
+ return None
155
+ return ExtensionSpec(
156
+ id=raw_id,
157
+ kind=raw_kind,
158
+ owner_app=raw_owner,
159
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
160
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
161
+ title=str(_payload_field(payload, "title", default="") or ""),
162
+ payload=dict(_payload_field(payload, "payload", default=None) or {}),
163
+ source=str(_payload_field(payload, "source", default="") or ""),
164
+ provides=tuple(_payload_field(payload, "provides", default=()) or ()),
165
+ conflicts=tuple(_payload_field(payload, "conflicts", default=()) or ()),
166
+ )
167
+
168
+
169
+ def _coerce_host_builder_spec(payload: Any, *, owner_hint: str) -> HostBuilderSpec | None:
170
+ builder = _payload_field(payload, "builder")
171
+ if builder is None:
172
+ return None
173
+ raw_id = _payload_field(payload, "id")
174
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
175
+ raw_kind = _payload_field(payload, "kind", default="host.builder")
176
+ if not isinstance(raw_id, str) or not raw_id.strip():
177
+ return None
178
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
179
+ return None
180
+ if str(raw_kind or "").strip() != "host.builder":
181
+ return None
182
+ return HostBuilderSpec(
183
+ id=raw_id,
184
+ owner_app=raw_owner,
185
+ builder=builder,
186
+ kind="host.builder",
187
+ runtime_kind=str(_payload_field(payload, "runtime_kind", default="web") or "web"),
188
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
189
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
190
+ title=str(_payload_field(payload, "title", default="") or ""),
191
+ )
192
+
193
+
194
+ def _coerce_settings_provider_spec(payload: Any, *, owner_hint: str) -> SettingsProviderSpec | None:
195
+ provider = _payload_field(payload, "provider")
196
+ if provider is None:
197
+ return None
198
+ raw_id = _payload_field(payload, "id")
199
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
200
+ raw_kind = _payload_field(payload, "kind", default="settings.provider")
201
+ if not isinstance(raw_id, str) or not raw_id.strip():
202
+ return None
203
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
204
+ return None
205
+ if str(raw_kind or "").strip() != "settings.provider":
206
+ return None
207
+ return SettingsProviderSpec(
208
+ id=raw_id,
209
+ owner_app=raw_owner,
210
+ provider=provider,
211
+ kind="settings.provider",
212
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
213
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
214
+ )
215
+
216
+
217
+ def _coerce_infra_provider_spec(payload: Any, *, owner_hint: str) -> InfraProviderSpec | None:
218
+ provider = _payload_field(payload, "provider")
219
+ if provider is None:
220
+ return None
221
+ raw_id = _payload_field(payload, "id")
222
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
223
+ raw_kind = _payload_field(payload, "kind", default="infra.provider")
224
+ if not isinstance(raw_id, str) or not raw_id.strip():
225
+ return None
226
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
227
+ return None
228
+ if str(raw_kind or "").strip() != "infra.provider":
229
+ return None
230
+ return InfraProviderSpec(
231
+ id=raw_id,
232
+ owner_app=raw_owner,
233
+ provider=provider,
234
+ kind="infra.provider",
235
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
236
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
237
+ title=str(_payload_field(payload, "title", default="") or ""),
238
+ )
239
+
240
+
241
+ def _coerce_runtime_manifest_contribution_spec(
242
+ payload: Any,
243
+ *,
244
+ owner_hint: str,
245
+ ) -> RuntimeManifestContributionSpec | None:
246
+ provider = _payload_field(payload, "provider")
247
+ if provider is None:
248
+ return None
249
+ raw_id = _payload_field(payload, "id")
250
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
251
+ raw_kind = _payload_field(payload, "kind", default="runtime.manifest_contribution")
252
+ if not isinstance(raw_id, str) or not raw_id.strip():
253
+ return None
254
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
255
+ return None
256
+ if str(raw_kind or "").strip() != "runtime.manifest_contribution":
257
+ return None
258
+ return RuntimeManifestContributionSpec(
259
+ id=raw_id,
260
+ owner_app=raw_owner,
261
+ provider=provider,
262
+ kind="runtime.manifest_contribution",
263
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
264
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
265
+ title=str(_payload_field(payload, "title", default="") or ""),
266
+ )
267
+
268
+
269
+ def _coerce_lifecycle_contribution_spec(payload: Any, *, owner_hint: str) -> LifecycleContributionSpec | None:
270
+ handler = _payload_field(payload, "handler")
271
+ phase = _payload_field(payload, "phase")
272
+ raw_id = _payload_field(payload, "id")
273
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
274
+ raw_kind = _payload_field(payload, "kind", default="lifecycle.contribution")
275
+ if handler is None:
276
+ return None
277
+ if not isinstance(raw_id, str) or not raw_id.strip():
278
+ return None
279
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
280
+ return None
281
+ if not isinstance(phase, str) or not phase.strip():
282
+ return None
283
+ if str(raw_kind or "").strip() != "lifecycle.contribution":
284
+ return None
285
+ return LifecycleContributionSpec(
286
+ id=raw_id,
287
+ owner_app=raw_owner,
288
+ handler=handler,
289
+ phase=phase.strip(),
290
+ kind="lifecycle.contribution",
291
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
292
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
293
+ )
294
+
295
+
296
+ def _coerce_worker_role_spec(payload: Any, *, owner_hint: str) -> WorkerRoleSpec | None:
297
+ tool_name = _payload_field(payload, "tool_name")
298
+ raw_id = _payload_field(payload, "id")
299
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
300
+ raw_kind = _payload_field(payload, "kind", default="worker.role")
301
+ if not isinstance(tool_name, str) or not tool_name.strip():
302
+ return None
303
+ if not isinstance(raw_id, str) or not raw_id.strip():
304
+ return None
305
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
306
+ return None
307
+ if str(raw_kind or "").strip() != "worker.role":
308
+ return None
309
+ return WorkerRoleSpec(
310
+ id=raw_id,
311
+ owner_app=raw_owner,
312
+ tool_name=tool_name.strip(),
313
+ kind="worker.role",
314
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
315
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
316
+ title=str(_payload_field(payload, "title", default="") or ""),
317
+ name=str(_payload_field(payload, "name", default="") or "").strip(),
318
+ auto_start=bool(_payload_field(payload, "auto_start", default=False)),
319
+ auto_start_setting=str(_payload_field(payload, "auto_start_setting", default="") or "").strip(),
320
+ restart=str(_payload_field(payload, "restart", default="always") or "always").strip() or "always",
321
+ worker_kind=(
322
+ str(
323
+ _payload_field(payload, "worker_kind", default="infrastructure_worker")
324
+ or "infrastructure_worker"
325
+ )
326
+ .strip()
327
+ or "infrastructure_worker"
328
+ ),
329
+ replica_count_setting=str(_payload_field(payload, "replica_count_setting", default="") or "").strip(),
330
+ description=str(_payload_field(payload, "description", default="") or "").strip(),
331
+ )
332
+
333
+
334
+ def _coerce_cli_tool_spec(payload: Any, *, owner_hint: str) -> CliToolSpec | None:
335
+ tool = _payload_field(payload, "tool")
336
+ command = _payload_field(payload, "command")
337
+ summary = _payload_field(payload, "summary")
338
+ raw_id = _payload_field(payload, "id")
339
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
340
+ raw_kind = _payload_field(payload, "kind", default="cli.tool")
341
+ if tool is None:
342
+ return None
343
+ if not isinstance(raw_id, str) or not raw_id.strip():
344
+ return None
345
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
346
+ return None
347
+ if str(raw_kind or "").strip() != "cli.tool":
348
+ return None
349
+ if isinstance(command, str):
350
+ command_parts = (command.strip(),) if command.strip() else ()
351
+ else:
352
+ command_parts = tuple(str(item).strip() for item in tuple(command or ()) if str(item).strip())
353
+ if not command_parts:
354
+ return None
355
+ return CliToolSpec(
356
+ id=raw_id,
357
+ owner_app=raw_owner,
358
+ command=command_parts,
359
+ summary=str(summary or "").strip(),
360
+ tool=tool,
361
+ kind="cli.tool",
362
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
363
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
364
+ title=str(_payload_field(payload, "title", default="") or ""),
365
+ )
366
+
367
+
368
+ def _coerce_page_profile_provider_spec(payload: Any, *, owner_hint: str) -> PageProfileProviderSpec | None:
369
+ provider = _payload_field(payload, "provider")
370
+ if provider is None:
371
+ return None
372
+ raw_id = _payload_field(payload, "id")
373
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
374
+ raw_kind = _payload_field(payload, "kind", default="page.profile_provider")
375
+ if not isinstance(raw_id, str) or not raw_id.strip():
376
+ return None
377
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
378
+ return None
379
+ if str(raw_kind or "").strip() != "page.profile_provider":
380
+ return None
381
+ return PageProfileProviderSpec(
382
+ id=raw_id,
383
+ owner_app=raw_owner,
384
+ provider=provider,
385
+ kind="page.profile_provider",
386
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
387
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
388
+ title=str(_payload_field(payload, "title", default="") or ""),
389
+ )
390
+
391
+
392
+ def _coerce_resource_contribution_spec(payload: Any, *, owner_hint: str) -> ResourceContributionSpec | None:
393
+ raw_kind = _payload_field(payload, "kind", default="resource.contribution")
394
+ if str(raw_kind or "").strip() != "resource.contribution":
395
+ return None
396
+ raw_id = _payload_field(payload, "id")
397
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
398
+ if not isinstance(raw_id, str) or not raw_id.strip():
399
+ return None
400
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
401
+ return None
402
+
403
+ def _read_tuple(field_name: str) -> tuple[Any, ...]:
404
+ value = _payload_field(payload, field_name, default=())
405
+ if value in (None, ()):
406
+ return ()
407
+ if isinstance(value, tuple):
408
+ return value
409
+ if isinstance(value, list):
410
+ return tuple(value)
411
+ return ()
412
+
413
+ def _read_locale_allowlist(field_name: str) -> tuple[str, ...]:
414
+ items = _read_tuple(field_name)
415
+ out: list[str] = []
416
+ for item in items:
417
+ if not isinstance(item, str) or not item.strip():
418
+ return ()
419
+ out.append(item.strip())
420
+ return tuple(out)
421
+
422
+ return ResourceContributionSpec(
423
+ id=raw_id,
424
+ owner_app=raw_owner,
425
+ templates_dir=str(_payload_field(payload, "templates_dir", default="templates") or "templates"),
426
+ static_dir=str(_payload_field(payload, "static_dir", default="static") or "static"),
427
+ locales_dir=str(_payload_field(payload, "locales_dir", default="locales") or "locales"),
428
+ migrations_dir=str(_payload_field(payload, "migrations_dir", default="migrations") or "migrations"),
429
+ kind="resource.contribution",
430
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
431
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
432
+ title=str(_payload_field(payload, "title", default="") or ""),
433
+ locale_bundles=tuple(dict(item) for item in _read_tuple("locale_bundles") if isinstance(item, dict)),
434
+ locale_dependencies=tuple(
435
+ dict(item) for item in _read_tuple("locale_dependencies") if isinstance(item, dict)
436
+ ),
437
+ locale_consumer_bindings=tuple(
438
+ dict(item) for item in _read_tuple("locale_consumer_bindings") if isinstance(item, dict)
439
+ ),
440
+ locale_profiles=tuple(dict(item) for item in _read_tuple("locale_profiles") if isinstance(item, dict)),
441
+ locale_overrides=tuple(dict(item) for item in _read_tuple("locale_overrides") if isinstance(item, dict)),
442
+ locale_allowlist=_read_locale_allowlist("locale_allowlist"),
443
+ )
444
+
445
+
446
+ def _coerce_protocol_registration_spec(payload: Any, *, owner_hint: str) -> ProtocolRegistrationSpec | None:
447
+ raw_kind = _payload_field(payload, "kind", default="protocol.registration")
448
+ if str(raw_kind or "").strip() != "protocol.registration":
449
+ return None
450
+ protocol_kind = _payload_field(payload, "protocol_kind")
451
+ spec_name = _payload_field(payload, "spec_name")
452
+ runtime_owner = _payload_field(payload, "runtime_owner")
453
+ raw_id = _payload_field(payload, "id")
454
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
455
+ if not isinstance(raw_id, str) or not raw_id.strip():
456
+ return None
457
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
458
+ return None
459
+ if not isinstance(protocol_kind, str) or not protocol_kind.strip():
460
+ return None
461
+ if not isinstance(spec_name, str) or not spec_name.strip():
462
+ return None
463
+ if not isinstance(runtime_owner, str) or not runtime_owner.strip():
464
+ return None
465
+ return ProtocolRegistrationSpec(
466
+ id=raw_id,
467
+ owner_app=raw_owner,
468
+ protocol_kind=protocol_kind.strip(),
469
+ spec_name=spec_name.strip(),
470
+ runtime_owner=runtime_owner.strip(),
471
+ required_fields=tuple(_payload_field(payload, "required_fields", default=()) or ()),
472
+ string_fields=tuple(_payload_field(payload, "string_fields", default=()) or ()),
473
+ import_fields=tuple(_payload_field(payload, "import_fields", default=()) or ()),
474
+ callable_fields=tuple(_payload_field(payload, "callable_fields", default=()) or ()),
475
+ validator=_payload_field(payload, "validator", default=None),
476
+ kind="protocol.registration",
477
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
478
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
479
+ title=str(_payload_field(payload, "title", default="") or ""),
480
+ )
481
+
482
+
483
+ def _coerce_protocol_extension_spec(payload: Any, *, owner_hint: str) -> ProtocolExtensionSpec | None:
484
+ raw_kind = _payload_field(payload, "kind")
485
+ if not isinstance(raw_kind, str) or not raw_kind.strip():
486
+ return None
487
+ if raw_kind.strip() in {
488
+ "host.builder",
489
+ "settings.provider",
490
+ "infra.provider",
491
+ "runtime.manifest_contribution",
492
+ "lifecycle.contribution",
493
+ "worker.role",
494
+ "cli.tool",
495
+ "page.profile_provider",
496
+ "resource.contribution",
497
+ "protocol.registration",
498
+ }:
499
+ return None
500
+ raw_id = _payload_field(payload, "id")
501
+ raw_owner = _payload_field(payload, "owner_app", default=owner_hint)
502
+ fields = _payload_field(payload, "fields", default=None)
503
+ if not isinstance(raw_id, str) or not raw_id.strip():
504
+ return None
505
+ if not isinstance(raw_owner, str) or not raw_owner.strip():
506
+ return None
507
+ if not isinstance(fields, Mapping):
508
+ return None
509
+ return ProtocolExtensionSpec(
510
+ id=raw_id,
511
+ owner_app=raw_owner,
512
+ kind=raw_kind.strip(),
513
+ fields={str(key): value for key, value in dict(fields).items()},
514
+ optional=bool(_payload_field(payload, "optional", default=False)),
515
+ api_version=str(_payload_field(payload, "api_version", default="v1") or "v1"),
516
+ priority=int(_payload_field(payload, "priority", default=100) or 100),
517
+ title=str(_payload_field(payload, "title", default="") or ""),
518
+ )
519
+
520
+
521
+ def _normalize_decl(payload: Any, *, owner_hint: str) -> FormalExtensionSpec:
522
+ if isinstance(payload, HostBuilderSpec):
523
+ return payload
524
+ if isinstance(payload, SettingsProviderSpec):
525
+ return payload
526
+ if isinstance(payload, InfraProviderSpec):
527
+ return payload
528
+ if isinstance(payload, RuntimeManifestContributionSpec):
529
+ return payload
530
+ if isinstance(payload, LifecycleContributionSpec):
531
+ return payload
532
+ if isinstance(payload, WorkerRoleSpec):
533
+ return payload
534
+ if isinstance(payload, CliToolSpec):
535
+ return payload
536
+ if isinstance(payload, PageProfileProviderSpec):
537
+ return _validate_page_profile_provider_spec(payload)
538
+ if isinstance(payload, ProtocolRegistrationSpec):
539
+ return payload
540
+ if isinstance(payload, ProtocolExtensionSpec):
541
+ return payload
542
+ if isinstance(payload, ExtensionSpec):
543
+ if payload.kind in {
544
+ "host.builder",
545
+ "settings.provider",
546
+ "infra.provider",
547
+ "runtime.manifest_contribution",
548
+ "lifecycle.contribution",
549
+ "worker.role",
550
+ "cli.tool",
551
+ "page.profile_provider",
552
+ "protocol.registration",
553
+ }:
554
+ raise PzError(
555
+ code="PZ_EXTENSION_DECL_INVALID",
556
+ message=(
557
+ f"App '{owner_hint}' must declare '{payload.kind}' via the dedicated formal spec "
558
+ "instead of generic ExtensionSpec."
559
+ ),
560
+ stage="extensions",
561
+ owner=owner_hint,
562
+ action="compile_extension_registry",
563
+ )
564
+ return payload
565
+ shaped_host_builder = _coerce_host_builder_spec(payload, owner_hint=owner_hint)
566
+ if shaped_host_builder is not None:
567
+ return shaped_host_builder
568
+ shaped_settings_provider = _coerce_settings_provider_spec(payload, owner_hint=owner_hint)
569
+ if shaped_settings_provider is not None:
570
+ return shaped_settings_provider
571
+ shaped_infra_provider = _coerce_infra_provider_spec(payload, owner_hint=owner_hint)
572
+ if shaped_infra_provider is not None:
573
+ return shaped_infra_provider
574
+ shaped_runtime_manifest_contribution = _coerce_runtime_manifest_contribution_spec(
575
+ payload,
576
+ owner_hint=owner_hint,
577
+ )
578
+ if shaped_runtime_manifest_contribution is not None:
579
+ return shaped_runtime_manifest_contribution
580
+ shaped_lifecycle_contribution = _coerce_lifecycle_contribution_spec(payload, owner_hint=owner_hint)
581
+ if shaped_lifecycle_contribution is not None:
582
+ return shaped_lifecycle_contribution
583
+ shaped_worker_role = _coerce_worker_role_spec(payload, owner_hint=owner_hint)
584
+ if shaped_worker_role is not None:
585
+ return shaped_worker_role
586
+ shaped_cli_tool = _coerce_cli_tool_spec(payload, owner_hint=owner_hint)
587
+ if shaped_cli_tool is not None:
588
+ return shaped_cli_tool
589
+ shaped_page_profile_provider = _coerce_page_profile_provider_spec(payload, owner_hint=owner_hint)
590
+ if shaped_page_profile_provider is not None:
591
+ return _validate_page_profile_provider_spec(shaped_page_profile_provider)
592
+ shaped_resource_contribution = _coerce_resource_contribution_spec(payload, owner_hint=owner_hint)
593
+ if shaped_resource_contribution is not None:
594
+ return shaped_resource_contribution
595
+ shaped_protocol_registration = _coerce_protocol_registration_spec(payload, owner_hint=owner_hint)
596
+ if shaped_protocol_registration is not None:
597
+ return shaped_protocol_registration
598
+ shaped_protocol_extension = _coerce_protocol_extension_spec(payload, owner_hint=owner_hint)
599
+ if shaped_protocol_extension is not None:
600
+ return shaped_protocol_extension
601
+ shaped_extension = _coerce_extension_spec(payload, owner_hint=owner_hint)
602
+ if shaped_extension is not None:
603
+ if shaped_extension.kind in {
604
+ "host.builder",
605
+ "settings.provider",
606
+ "infra.provider",
607
+ "runtime.manifest_contribution",
608
+ "lifecycle.contribution",
609
+ "worker.role",
610
+ "cli.tool",
611
+ "page.profile_provider",
612
+ "resource.contribution",
613
+ "protocol.registration",
614
+ }:
615
+ raise PzError(
616
+ code="PZ_EXTENSION_DECL_INVALID",
617
+ message=(
618
+ f"App '{owner_hint}' must declare '{shaped_extension.kind}' via the dedicated formal spec "
619
+ "instead of generic ExtensionSpec."
620
+ ),
621
+ stage="extensions",
622
+ owner=owner_hint,
623
+ action="compile_extension_registry",
624
+ )
625
+ return shaped_extension
626
+ raise PzError(
627
+ code="PZ_EXTENSION_DECL_INVALID",
628
+ message=(
629
+ f"Extension declaration from '{owner_hint}' must be a formal protocol object "
630
+ "ExtensionSpec, HostBuilderSpec, SettingsProviderSpec, InfraProviderSpec, "
631
+ "RuntimeManifestContributionSpec, LifecycleContributionSpec, WorkerRoleSpec, "
632
+ "CliToolSpec, PageProfileProviderSpec, "
633
+ "ResourceContributionSpec, "
634
+ "ProtocolRegistrationSpec, or ProtocolExtensionSpec."
635
+ ),
636
+ stage="extensions",
637
+ owner=owner_hint,
638
+ action="compile_extension_registry",
639
+ )
640
+
641
+
642
+ def _load_app_declared_extensions(app_registry: AppRegistry) -> tuple[FormalExtensionSpec, ...]:
643
+ out: list[FormalExtensionSpec] = []
644
+ for app in app_registry.apps:
645
+ app_obj = load_app_object_from_decl(app)
646
+ normalized: list[FormalExtensionSpec] = []
647
+ extensions_factory = getattr(app_obj, "extensions", None)
648
+ if extensions_factory is None:
649
+ declared = ()
650
+ else:
651
+ if not callable(extensions_factory):
652
+ raise PzError(
653
+ code="PZ_APP_EXTENSIONS_INVALID",
654
+ message=f"App '{app.app_id}' exposes non-callable extensions().",
655
+ stage="extensions",
656
+ owner=app.app_id,
657
+ action="compile_extension_registry",
658
+ )
659
+ declared = extensions_factory()
660
+ if declared not in (None, (), []):
661
+ if not isinstance(declared, (list, tuple)):
662
+ raise PzError(
663
+ code="PZ_APP_EXTENSIONS_INVALID",
664
+ message=f"App '{app.app_id}' extensions() must return a list or tuple.",
665
+ stage="extensions",
666
+ owner=app.app_id,
667
+ action="compile_extension_registry",
668
+ )
669
+ normalized.extend(_normalize_decl(payload, owner_hint=app.app_id) for payload in declared)
670
+ normalized.extend(
671
+ _synthesized_lifecycle_contributions(
672
+ app,
673
+ app_obj,
674
+ declared=tuple(normalized),
675
+ )
676
+ )
677
+ out.extend(normalized)
678
+ return tuple(out)
679
+
680
+
681
+ def compile_extension_registry(
682
+ settings: SettingsSnapshot,
683
+ app_registry: AppRegistry,
684
+ ) -> ExtensionRegistry:
685
+ project_dir = getattr(settings, "project_dir", None)
686
+ import_roots = getattr(settings, "import_roots", ())
687
+ with active_project_import_root(
688
+ project_dir,
689
+ extra_import_roots=import_roots,
690
+ ):
691
+ registry = ExtensionRegistry(extensions=_load_app_declared_extensions(app_registry))
692
+ registry.validate()
693
+ return registry
694
+
695
+
696
+ __all__ = ["compile_extension_registry"]