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,227 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ import importlib
5
+ import os
6
+ from pathlib import Path
7
+ import sys
8
+ from typing import Iterable
9
+
10
+ from pz_core.assembly.app_discovery import iter_visible_app_projections
11
+ from pz_core.assembly.project.context import ProjectContext
12
+ from pz_core.core.errors import PzError
13
+ from pz_core.runtime.environment_integrity import (
14
+ validate_current_environment,
15
+ validate_project_config_identity,
16
+ )
17
+ from pz_core.utils.project_jsonc import PROJECT_JSONC_NAME
18
+
19
+
20
+ def _is_project_root(candidate: Path) -> bool:
21
+ return (candidate / PROJECT_JSONC_NAME).is_file()
22
+
23
+
24
+ def _is_under_source_root(candidate: Path) -> bool:
25
+ return ".sources" in candidate.parts
26
+
27
+
28
+ def _resolve_project_root(candidate: Path) -> Path:
29
+ expanded = candidate.expanduser()
30
+ search_roots: list[Path] = []
31
+ if expanded.is_absolute():
32
+ search_roots.append(expanded)
33
+ else:
34
+ search_roots.append((Path.cwd() / expanded).absolute())
35
+ resolved = expanded.resolve()
36
+ if resolved not in search_roots:
37
+ search_roots.append(resolved)
38
+ for search_root in search_roots:
39
+ for current in (search_root, *search_root.parents):
40
+ validate_project_config_identity(current)
41
+ if _is_project_root(current):
42
+ return current.resolve()
43
+ if any(_is_under_source_root(root) for root in search_roots):
44
+ raise PzError(
45
+ code="PZ_PROJECT_DIR_REQUIRED_FOR_SOURCE_ROOT",
46
+ message=(
47
+ "Cannot infer project_dir from a package source root. "
48
+ f"Set PZ_PROJECT_DIR or pass an explicit project_dir that resolves to a project with {PROJECT_JSONC_NAME}."
49
+ ),
50
+ stage="project",
51
+ owner="assembly.project",
52
+ action="resolve_project_context",
53
+ )
54
+ return resolved
55
+
56
+
57
+ def _discover_project_dir_from_cwd() -> Path:
58
+ return _resolve_project_root(Path.cwd())
59
+
60
+
61
+ def _read_project_dir_source(project_dir: str | os.PathLike[str]) -> str:
62
+ if not isinstance(project_dir, (str, os.PathLike)):
63
+ raise PzError(
64
+ code="PZ_PROJECT_DIR_INVALID",
65
+ message="project_dir must be a non-empty path when provided.",
66
+ stage="project",
67
+ owner="assembly.project",
68
+ action="resolve_project_context",
69
+ )
70
+ value = os.fspath(project_dir)
71
+ if not isinstance(value, str) or not value.strip():
72
+ raise PzError(
73
+ code="PZ_PROJECT_DIR_INVALID",
74
+ message="project_dir must be a non-empty path when provided.",
75
+ stage="project",
76
+ owner="assembly.project",
77
+ action="resolve_project_context",
78
+ )
79
+ return value
80
+
81
+
82
+ def _read_project_dir_env(name: str) -> str | None:
83
+ raw = os.getenv(name)
84
+ if raw is None:
85
+ return None
86
+ if not raw.strip():
87
+ raise PzError(
88
+ code="PZ_PROJECT_DIR_INVALID",
89
+ message=f"{name} must be a non-empty path when configured.",
90
+ stage="project",
91
+ owner="assembly.project",
92
+ action="resolve_project_context",
93
+ )
94
+ return raw.strip()
95
+
96
+
97
+ def resolve_project_context(
98
+ project_dir: str | os.PathLike[str] | None = None,
99
+ ) -> ProjectContext:
100
+ validate_current_environment(os.environ)
101
+ env_project_dir = _read_project_dir_env("PZ_PROJECT_DIR")
102
+ source = (
103
+ _read_project_dir_source(project_dir)
104
+ if project_dir is not None
105
+ else env_project_dir or _discover_project_dir_from_cwd()
106
+ )
107
+ try:
108
+ root = _resolve_project_root(Path(source))
109
+ except PzError as exc:
110
+ if (
111
+ project_dir is None
112
+ or env_project_dir is None
113
+ or exc.code != "PZ_PROJECT_DIR_REQUIRED_FOR_SOURCE_ROOT"
114
+ ):
115
+ raise
116
+ root = _resolve_project_root(Path(env_project_dir))
117
+ runtime_root = root / ".runtime"
118
+ project_config_path = root / PROJECT_JSONC_NAME
119
+ return ProjectContext(
120
+ project_dir=root,
121
+ project_config_path=project_config_path,
122
+ env_file_path=root / ".env",
123
+ cache_dir=runtime_root / "cache",
124
+ runtime_dir=runtime_root / "run",
125
+ asset_dir=root / ".assets",
126
+ )
127
+
128
+
129
+ def _module_belongs_to_roots(module: object, roots: set[str]) -> bool:
130
+ module_file = getattr(module, "__file__", None)
131
+ if isinstance(module_file, str) and module_file:
132
+ resolved_file = str(Path(module_file).resolve())
133
+ for root in roots:
134
+ if resolved_file == root or resolved_file.startswith(root + os.sep):
135
+ return True
136
+
137
+ search_locations = getattr(module, "__path__", None)
138
+ if search_locations is None:
139
+ return False
140
+ for raw_location in search_locations:
141
+ resolved_location = str(Path(raw_location).resolve())
142
+ for root in roots:
143
+ if resolved_location == root or resolved_location.startswith(root + os.sep):
144
+ return True
145
+ return False
146
+
147
+
148
+ def _managed_top_level_modules(
149
+ project_root: Path, import_roots: Iterable[Path]
150
+ ) -> set[str]:
151
+ module_names: set[str] = set()
152
+ if (project_root / "apps" / "__init__.py").is_file():
153
+ module_names.add("apps")
154
+ module_names.update(
155
+ projection.module_path.split(".", 1)[0]
156
+ for projection in iter_visible_app_projections(project_root)
157
+ )
158
+ for import_root in import_roots:
159
+ resolved_import_root = import_root.resolve()
160
+ if (
161
+ not resolved_import_root.is_dir()
162
+ or resolved_import_root == project_root
163
+ or resolved_import_root.is_relative_to(project_root)
164
+ ):
165
+ continue
166
+ for child in resolved_import_root.iterdir():
167
+ if child.is_dir() and (child / "__init__.py").is_file():
168
+ module_names.add(child.name)
169
+ return module_names
170
+
171
+
172
+ @contextmanager
173
+ def active_project_import_root(
174
+ project_dir: str | os.PathLike[str] | None,
175
+ *,
176
+ extra_import_roots: Iterable[str | os.PathLike[str]] = (),
177
+ preserve_loaded_modules: bool = False,
178
+ ):
179
+ if project_dir is None:
180
+ yield
181
+ return
182
+ root = Path(project_dir).expanduser().resolve()
183
+ normalized_roots = [root]
184
+ seen_roots = {str(root)}
185
+ for raw_root in extra_import_roots:
186
+ candidate = Path(raw_root).expanduser().resolve()
187
+ candidate_key = str(candidate)
188
+ if candidate_key in seen_roots:
189
+ continue
190
+ seen_roots.add(candidate_key)
191
+ normalized_roots.append(candidate)
192
+ original_path = list(sys.path)
193
+ managed_modules = _managed_top_level_modules(root, normalized_roots[1:])
194
+ saved_modules = {
195
+ name: module
196
+ for name, module in sys.modules.items()
197
+ if any(
198
+ name == target or name.startswith(target + ".")
199
+ for target in managed_modules
200
+ )
201
+ }
202
+ try:
203
+ root_strings = {str(item) for item in normalized_roots}
204
+ sys.path[:] = [entry for entry in original_path if entry not in root_strings]
205
+ for candidate in reversed(normalized_roots):
206
+ sys.path.insert(0, str(candidate))
207
+ for name, module in saved_modules.items():
208
+ if not _module_belongs_to_roots(module, root_strings):
209
+ sys.modules.pop(name, None)
210
+ importlib.invalidate_caches()
211
+ yield
212
+ finally:
213
+ sys.path[:] = original_path
214
+ if preserve_loaded_modules:
215
+ importlib.invalidate_caches()
216
+ else:
217
+ for name in list(sys.modules):
218
+ if any(
219
+ name == target or name.startswith(target + ".")
220
+ for target in managed_modules
221
+ ):
222
+ sys.modules.pop(name, None)
223
+ sys.modules.update(saved_modules)
224
+ importlib.invalidate_caches()
225
+
226
+
227
+ __all__ = ["ProjectContext", "active_project_import_root", "resolve_project_context"]
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from .compiler import (
4
+ ResourceManifest,
5
+ ResourceItem,
6
+ ResourceSource,
7
+ build_resource_source,
8
+ compile_resource_manifest,
9
+ discover_resource_sources,
10
+ )
11
+
12
+ __all__ = [
13
+ "ResourceManifest",
14
+ "ResourceItem",
15
+ "ResourceSource",
16
+ "build_resource_source",
17
+ "compile_resource_manifest",
18
+ "discover_resource_sources",
19
+ ]
@@ -0,0 +1,310 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from importlib.util import find_spec
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Iterable
8
+
9
+ from pz_core.assembly.apps import AppRegistry
10
+ from pz_core.assembly.resources.settings import DEFAULT_RESOURCE_ITEMS, read_resource_settings
11
+ from pz_core.assembly.settings import SettingsSnapshot
12
+ from pz_core.core.errors import PzError
13
+ from pz_core.core.extensions import ExtensionRegistry, ResourceContributionSpec
14
+ from pz_core.assembly.project import ProjectContext
15
+ from pz_core.infra.settings import ResourceItemContract
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class ResourceItem:
20
+ name: str
21
+ source_dirs: tuple[Path, ...]
22
+ output_dir: Path
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class ResourceSource:
27
+ app_id: str
28
+ module_path: str
29
+ package_root: Path
30
+ items: tuple[ResourceItem, ...]
31
+
32
+
33
+ @dataclass(frozen=True, slots=True)
34
+ class ResourceManifest:
35
+ app_order: tuple[str, ...]
36
+ manifest_path: Path
37
+ output_root: Path
38
+ items: tuple[ResourceItem, ...]
39
+ staging_root: Path
40
+ lock_path: Path
41
+ sources: tuple[ResourceSource, ...]
42
+ resource_contributions: tuple[ResourceContributionSpec, ...] = ()
43
+
44
+
45
+ def _resource_import_root(module_path: str, *, entrypoint: str, owner: str) -> str:
46
+ module_name, _, attr_name = entrypoint.partition(":")
47
+ if attr_name == "app" and module_name.endswith(".apps"):
48
+ return module_name[:-5]
49
+ raise PzError(
50
+ code="PZ_RESOURCE_APP_ENTRYPOINT_INVALID",
51
+ message=(
52
+ f"App '{owner}' entrypoint '{entrypoint}' must use '<pkg>.apps:app' "
53
+ "before compiling resource manifests."
54
+ ),
55
+ stage="resources",
56
+ owner=owner,
57
+ action="compile_resource_manifest",
58
+ )
59
+
60
+
61
+ def _package_root(module_path: str, *, entrypoint: str, owner: str) -> Path:
62
+ module_name, _, attr_name = entrypoint.partition(":")
63
+ if attr_name != "app" or not module_name.endswith(".apps"):
64
+ _resource_import_root(module_path, entrypoint=entrypoint, owner=owner)
65
+ raise PzError(
66
+ code="PZ_RESOURCE_APP_ENTRYPOINT_INVALID",
67
+ message=(
68
+ f"App '{owner}' entrypoint '{entrypoint}' must use '<pkg>.apps:app' "
69
+ "before compiling resource manifests."
70
+ ),
71
+ stage="resources",
72
+ owner=owner,
73
+ action="compile_resource_manifest",
74
+ )
75
+ spec = find_spec(module_name)
76
+ if spec is None:
77
+ raise PzError(
78
+ code="PZ_RESOURCE_APP_NOT_FOUND",
79
+ message=f"Cannot resolve resource package for app '{module_path}'.",
80
+ stage="resources",
81
+ owner=module_path,
82
+ action="compile_resource_manifest",
83
+ )
84
+ if spec.origin:
85
+ return Path(spec.origin).resolve().parent
86
+ if spec.submodule_search_locations:
87
+ return Path(next(iter(spec.submodule_search_locations))).resolve()
88
+ import_root = _resource_import_root(module_path, entrypoint=entrypoint, owner=owner)
89
+ spec = find_spec(import_root)
90
+ if spec is None:
91
+ raise PzError(
92
+ code="PZ_RESOURCE_APP_NOT_FOUND",
93
+ message=f"Cannot resolve package root for app '{module_path}'.",
94
+ stage="resources",
95
+ owner=module_path,
96
+ action="compile_resource_manifest",
97
+ )
98
+ if spec.origin:
99
+ return Path(spec.origin).resolve().parent
100
+ if spec.submodule_search_locations:
101
+ return Path(next(iter(spec.submodule_search_locations))).resolve()
102
+ raise PzError(
103
+ code="PZ_RESOURCE_APP_NOT_FOUND",
104
+ message=f"Cannot resolve package root for app '{module_path}'.",
105
+ stage="resources",
106
+ owner=module_path,
107
+ action="compile_resource_manifest",
108
+ )
109
+
110
+
111
+ def _optional_dir(package_root: Path, name: str) -> Path | None:
112
+ path = package_root / name
113
+ if path.is_dir():
114
+ return path
115
+ if path.exists():
116
+ raise PzError(
117
+ code="PZ_RESOURCE_DIR_INVALID",
118
+ message=f"Resource path '{path.name}' for package '{package_root.name}' must be a directory.",
119
+ stage="resources",
120
+ owner=str(package_root),
121
+ action="compile_resource_manifest",
122
+ )
123
+ return None
124
+
125
+
126
+ _IGNORED_RESOURCE_PARTS = {"tests", "test", "docs", "__pycache__"}
127
+
128
+ def _default_resource_items() -> tuple[ResourceItemContract, ...]:
129
+ return tuple(
130
+ ResourceItemContract(
131
+ name=item["name"],
132
+ output_dir=Path("."),
133
+ )
134
+ for item in DEFAULT_RESOURCE_ITEMS
135
+ )
136
+
137
+
138
+ def _discover_resource_dirs(
139
+ package_root: Path,
140
+ *,
141
+ resource_items: tuple[ResourceItemContract, ...],
142
+ ) -> dict[str, tuple[Path, ...]]:
143
+ resource_item_names = tuple(item.name for item in resource_items)
144
+ collected: dict[str, list[Path]] = {name: [] for name in resource_item_names}
145
+ root_resolved: dict[str, Path | None] = {}
146
+
147
+ for name in resource_item_names:
148
+ root_dir = _optional_dir(package_root, name)
149
+ if root_dir is not None:
150
+ resolved = root_dir.resolve()
151
+ collected[name].append(resolved)
152
+ root_resolved[name] = resolved
153
+ else:
154
+ root_resolved[name] = None
155
+
156
+ for current_root, dirnames, filenames in os.walk(package_root):
157
+ current_path = Path(current_root)
158
+ if current_path.name in resource_item_names:
159
+ dirnames[:] = []
160
+ continue
161
+ dirnames[:] = sorted(
162
+ name
163
+ for name in dirnames
164
+ if name not in _IGNORED_RESOURCE_PARTS
165
+ )
166
+ for file_name in filenames:
167
+ if file_name in resource_item_names:
168
+ raise PzError(
169
+ code="PZ_RESOURCE_DIR_INVALID",
170
+ message=f"Resource path '{file_name}' for package '{package_root.name}' must be a directory.",
171
+ stage="resources",
172
+ owner=str(package_root),
173
+ action="compile_resource_manifest",
174
+ )
175
+ for dir_name in dirnames:
176
+ if dir_name not in resource_item_names:
177
+ continue
178
+ candidate = (current_path / dir_name).resolve()
179
+ if root_resolved[dir_name] is not None and candidate == root_resolved[dir_name]:
180
+ continue
181
+ collected[dir_name].append(candidate)
182
+
183
+ return {name: tuple(paths) for name, paths in collected.items()}
184
+
185
+
186
+ def build_resource_source(
187
+ *,
188
+ app_id: str,
189
+ module_path: str,
190
+ package_root: Path,
191
+ resource_items: tuple[ResourceItemContract, ...],
192
+ output_root: Path | None = None,
193
+ ) -> ResourceSource:
194
+ discovered = _discover_resource_dirs(package_root, resource_items=resource_items)
195
+ resolved_output_root = output_root if output_root is not None else Path(".")
196
+ return ResourceSource(
197
+ app_id=app_id,
198
+ module_path=module_path,
199
+ package_root=package_root,
200
+ items=tuple(
201
+ ResourceItem(
202
+ name=item.name,
203
+ source_dirs=discovered.get(item.name, ()),
204
+ output_dir=resolved_output_root / item.name,
205
+ )
206
+ for item in resource_items
207
+ ),
208
+ )
209
+
210
+
211
+ def discover_resource_sources(
212
+ module_paths: Iterable[str],
213
+ *,
214
+ resource_items: tuple[ResourceItemContract, ...] | None = None,
215
+ ) -> tuple[ResourceSource, ...]:
216
+ resolved_resource_items = resource_items or _default_resource_items()
217
+ sources: list[ResourceSource] = []
218
+ for raw_module_path in module_paths:
219
+ module_path = str(raw_module_path).split(":", 1)[0].strip()
220
+ if not module_path:
221
+ continue
222
+ try:
223
+ spec = find_spec(module_path)
224
+ except ModuleNotFoundError:
225
+ continue
226
+ if spec is None:
227
+ continue
228
+ if spec.submodule_search_locations:
229
+ package_root = Path(next(iter(spec.submodule_search_locations))).resolve()
230
+ elif spec.origin:
231
+ package_root = Path(spec.origin).resolve().parent
232
+ else:
233
+ continue
234
+ sources.append(
235
+ build_resource_source(
236
+ app_id=module_path.split(".")[-1],
237
+ module_path=module_path,
238
+ package_root=package_root,
239
+ resource_items=resolved_resource_items,
240
+ output_root=Path("."),
241
+ )
242
+ )
243
+ return tuple(sources)
244
+
245
+ def _compile_resource_contribution_specs(
246
+ extension_registry: ExtensionRegistry | None,
247
+ ) -> tuple[ResourceContributionSpec, ...]:
248
+ if extension_registry is None:
249
+ return ()
250
+ return tuple(
251
+ item
252
+ for item in extension_registry.by_kind("resource.contribution")
253
+ if isinstance(item, ResourceContributionSpec)
254
+ )
255
+
256
+
257
+ def compile_resource_manifest(
258
+ project: ProjectContext,
259
+ settings: SettingsSnapshot,
260
+ app_registry: AppRegistry,
261
+ extension_registry: ExtensionRegistry | None = None,
262
+ ) -> ResourceManifest:
263
+ resource_settings = read_resource_settings(
264
+ settings,
265
+ project,
266
+ stage="resources",
267
+ owner="resources",
268
+ action="compile_resource_manifest",
269
+ )
270
+ output_root = resource_settings.output_dir
271
+ manifest_path = resource_settings.manifest_path
272
+ staging_root = resource_settings.staging_dir
273
+ lock_path = resource_settings.lock_path
274
+ items = tuple(
275
+ ResourceItem(
276
+ name=item.name,
277
+ source_dirs=(),
278
+ output_dir=item.output_dir,
279
+ )
280
+ for item in resource_settings.items
281
+ )
282
+
283
+ sources: list[ResourceSource] = []
284
+ for app in app_registry.apps:
285
+ package_root = _package_root(
286
+ app.module_path,
287
+ entrypoint=app.entrypoint,
288
+ owner=app.app_id,
289
+ )
290
+ sources.append(
291
+ build_resource_source(
292
+ app_id=app.app_id,
293
+ module_path=app.module_path,
294
+ package_root=package_root,
295
+ resource_items=resource_settings.items,
296
+ output_root=output_root,
297
+ )
298
+ )
299
+
300
+ resource_contributions = _compile_resource_contribution_specs(extension_registry)
301
+ return ResourceManifest(
302
+ app_order=tuple(app.app_id for app in app_registry.apps),
303
+ manifest_path=manifest_path,
304
+ output_root=output_root,
305
+ items=items,
306
+ staging_root=staging_root,
307
+ lock_path=lock_path,
308
+ sources=tuple(sources),
309
+ resource_contributions=resource_contributions,
310
+ )
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from pz_core.assembly.project import ProjectContext
6
+ from pz_core.assembly.settings import SettingsSnapshot
7
+ from pz_core.core.errors import PzError
8
+ from pz_core.infra.settings import ResourceItemContract, ResourceSettings
9
+
10
+ DEFAULT_RESOURCE_ITEMS = (
11
+ {"name": "templates"},
12
+ {"name": "static"},
13
+ {"name": "locales"},
14
+ {"name": "pages"},
15
+ {"name": "template_config"},
16
+ {"name": "migrations"},
17
+ )
18
+
19
+
20
+ def _resource_environment(settings: SettingsSnapshot) -> str:
21
+ raw = settings.get("project", "pz_env", default="develop")
22
+ value = str(raw or "").strip().lower()
23
+ return value or "develop"
24
+
25
+
26
+ def read_resource_settings(
27
+ settings: SettingsSnapshot,
28
+ project: ProjectContext,
29
+ *,
30
+ stage: str,
31
+ owner: str,
32
+ action: str,
33
+ ) -> ResourceSettings:
34
+ raw_resources = settings.get("resources", default={})
35
+ if not isinstance(raw_resources, dict):
36
+ raise PzError(
37
+ code="PZ_RESOURCE_SETTINGS_INVALID",
38
+ message="resources must be declared as a table.",
39
+ stage=stage,
40
+ owner=owner,
41
+ action=action,
42
+ )
43
+
44
+ raw_items = raw_resources.get("items", DEFAULT_RESOURCE_ITEMS)
45
+ if not isinstance(raw_items, (list, tuple)):
46
+ raise PzError(
47
+ code="PZ_RESOURCE_SETTINGS_INVALID",
48
+ message="resources.items must be declared as an array of objects with non-empty name fields.",
49
+ stage=stage,
50
+ owner=owner,
51
+ action=action,
52
+ )
53
+ item_names: list[str] = []
54
+ parsed_items: list[ResourceItemContract] = []
55
+ for item in raw_items:
56
+ if not isinstance(item, dict):
57
+ raise PzError(
58
+ code="PZ_RESOURCE_SETTINGS_INVALID",
59
+ message="resources.items must be declared as an array of objects with non-empty name fields.",
60
+ stage=stage,
61
+ owner=owner,
62
+ action=action,
63
+ )
64
+ raw_name = item.get("name")
65
+ if not isinstance(raw_name, str) or not raw_name.strip():
66
+ raise PzError(
67
+ code="PZ_RESOURCE_SETTINGS_INVALID",
68
+ message="resources.items must be declared as an array of objects with non-empty name fields.",
69
+ stage=stage,
70
+ owner=owner,
71
+ action=action,
72
+ )
73
+ normalized = raw_name.strip()
74
+ if normalized in item_names:
75
+ continue
76
+ item_names.append(normalized)
77
+ parsed_items.append(
78
+ ResourceItemContract(
79
+ name=normalized,
80
+ output_dir=Path("."),
81
+ )
82
+ )
83
+ if not parsed_items:
84
+ raise PzError(
85
+ code="PZ_RESOURCE_SETTINGS_INVALID",
86
+ message="resources.items must declare at least one resource item.",
87
+ stage=stage,
88
+ owner=owner,
89
+ action=action,
90
+ )
91
+
92
+ output_root_name = str(raw_resources.get("output_root", "resources") or "resources").strip()
93
+ manifest_name = str(raw_resources.get("manifest_name", "manifest.json") or "manifest.json").strip()
94
+ staging_name = str(raw_resources.get("staging_dir", "staging") or "staging").strip()
95
+ lock_name = str(raw_resources.get("lock_name", "resources.lock") or "resources.lock").strip()
96
+ if not output_root_name or not manifest_name or not staging_name or not lock_name:
97
+ raise PzError(
98
+ code="PZ_RESOURCE_SETTINGS_INVALID",
99
+ message=(
100
+ "resources.output_root, resources.manifest_name, "
101
+ "resources.staging_dir, and resources.lock_name must be non-empty strings."
102
+ ),
103
+ stage=stage,
104
+ owner=owner,
105
+ action=action,
106
+ )
107
+ env_name = _resource_environment(settings)
108
+ output_dir = project.cache_dir / env_name / output_root_name
109
+ return ResourceSettings(
110
+ items=tuple(
111
+ ResourceItemContract(
112
+ name=item.name,
113
+ output_dir=output_dir / item.name,
114
+ )
115
+ for item in parsed_items
116
+ ),
117
+ output_dir=output_dir,
118
+ lock_path=project.runtime_dir / env_name / "locks" / lock_name,
119
+ manifest_path=output_dir / manifest_name,
120
+ staging_dir=output_dir / staging_name,
121
+ manifest_name=manifest_name,
122
+ lock_name=lock_name,
123
+ )
124
+
125
+
126
+ __all__ = [
127
+ "DEFAULT_RESOURCE_ITEMS",
128
+ "read_resource_settings",
129
+ ]
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from pz_core.core.routing import RouteDecl, RouteManifest
4
+
5
+ from .compiler import compile_route_manifest
6
+
7
+ __all__ = ["RouteDecl", "RouteManifest", "compile_route_manifest"]