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,508 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, is_dataclass
4
+ import inspect
5
+ from typing import Mapping
6
+ from typing import Any
7
+
8
+ from pz_core.assembly.app_discovery import read_static_app_declaration
9
+ from pz_core.assembly.apps import AppRegistry, load_app_entrypoint_object_from_decl
10
+ from pz_core.assembly.project import active_project_import_root
11
+ from pz_core.assembly.settings import SettingsSnapshot
12
+ from pz_core.core.errors import PzError
13
+ from pz_core.core.middlewares import MiddlewareDecl, MiddlewareManifest
14
+
15
+
16
+ def _read_required_string(
17
+ value: object,
18
+ *,
19
+ code: str,
20
+ message: str,
21
+ owner: str,
22
+ ) -> str:
23
+ if not isinstance(value, str) or not value.strip():
24
+ raise PzError(
25
+ code=code,
26
+ message=message,
27
+ stage="middlewares",
28
+ owner=owner,
29
+ action="compile_middleware_manifest",
30
+ )
31
+ return value.strip()
32
+
33
+
34
+ def _read_optional_string(
35
+ value: object,
36
+ *,
37
+ default: str | None,
38
+ code: str,
39
+ message: str,
40
+ owner: str,
41
+ ) -> str | None:
42
+ if value is None:
43
+ return default
44
+ if not isinstance(value, str) or not value.strip():
45
+ raise PzError(
46
+ code=code,
47
+ message=message,
48
+ stage="middlewares",
49
+ owner=owner,
50
+ action="compile_middleware_manifest",
51
+ )
52
+ return value.strip()
53
+
54
+
55
+ def _read_priority(
56
+ value: object,
57
+ *,
58
+ default: int,
59
+ code: str,
60
+ message: str,
61
+ owner: str,
62
+ ) -> int:
63
+ if value is None:
64
+ return int(default)
65
+ if isinstance(value, bool):
66
+ raise PzError(
67
+ code=code,
68
+ message=message,
69
+ stage="middlewares",
70
+ owner=owner,
71
+ action="compile_middleware_manifest",
72
+ )
73
+ if isinstance(value, int):
74
+ return int(value)
75
+ if isinstance(value, str) and value.strip():
76
+ try:
77
+ return int(value.strip())
78
+ except ValueError as exc:
79
+ raise PzError(
80
+ code=code,
81
+ message=message,
82
+ stage="middlewares",
83
+ owner=owner,
84
+ action="compile_middleware_manifest",
85
+ ) from exc
86
+ raise PzError(
87
+ code=code,
88
+ message=message,
89
+ stage="middlewares",
90
+ owner=owner,
91
+ action="compile_middleware_manifest",
92
+ )
93
+
94
+
95
+ def _normalize_import_path(import_path: object, *, owner: str) -> str:
96
+ value = _read_required_string(
97
+ import_path,
98
+ code="PZ_MIDDLEWARE_IMPORT_INVALID",
99
+ message=f"Middleware '{owner}' must declare import/factory as a non-empty string path.",
100
+ owner=owner,
101
+ )
102
+ if ":" in value:
103
+ module_name, _, attr_name = value.partition(":")
104
+ else:
105
+ module_name, _, attr_name = value.rpartition(".")
106
+ if not module_name or not attr_name:
107
+ raise PzError(
108
+ code="PZ_MIDDLEWARE_IMPORT_INVALID",
109
+ message=f"Middleware import path '{import_path}' must use '<module>:<attr>' format.",
110
+ stage="middlewares",
111
+ owner=owner,
112
+ action="compile_middleware_manifest",
113
+ )
114
+ return f"{module_name}:{attr_name}"
115
+
116
+
117
+ def _normalize_validated_params(
118
+ validated: object,
119
+ raw_params: Mapping[str, Any],
120
+ *,
121
+ middleware_id: str,
122
+ owner: str,
123
+ ) -> dict[str, Any]:
124
+ if validated is None:
125
+ return {}
126
+ model_dump = getattr(validated, "model_dump", None)
127
+ if callable(model_dump):
128
+ payload = model_dump()
129
+ if isinstance(payload, dict):
130
+ return dict(payload)
131
+ dict_method = getattr(validated, "dict", None)
132
+ if callable(dict_method):
133
+ payload = dict_method()
134
+ if isinstance(payload, dict):
135
+ return dict(payload)
136
+ if is_dataclass(validated):
137
+ payload = asdict(validated)
138
+ if isinstance(payload, dict):
139
+ return dict(payload)
140
+ if isinstance(validated, Mapping):
141
+ return dict(validated)
142
+ extracted: dict[str, Any] = {}
143
+ for key in raw_params:
144
+ if hasattr(validated, key):
145
+ extracted[str(key)] = getattr(validated, key)
146
+ if len(extracted) == len(raw_params):
147
+ return extracted
148
+ try:
149
+ payload = vars(validated)
150
+ except TypeError as exc:
151
+ raise PzError(
152
+ code="PZ_MIDDLEWARE_PARAMS_INVALID",
153
+ message=(
154
+ f"Middleware '{middleware_id}' params_model must expose normalized params via "
155
+ "model_dump(), dict(), dataclass fields, Mapping semantics, or matching attributes."
156
+ ),
157
+ stage="middlewares",
158
+ owner=owner,
159
+ action="compile_middleware_manifest",
160
+ ) from exc
161
+ if isinstance(payload, dict):
162
+ return dict(payload)
163
+ raise PzError(
164
+ code="PZ_MIDDLEWARE_PARAMS_INVALID",
165
+ message=(
166
+ f"Middleware '{middleware_id}' params_model must expose normalized params via "
167
+ "model_dump(), dict(), dataclass fields, Mapping semantics, or matching attributes."
168
+ ),
169
+ stage="middlewares",
170
+ owner=owner,
171
+ action="compile_middleware_manifest",
172
+ )
173
+
174
+
175
+ def _validate_params_model(
176
+ params_model_path: str | None,
177
+ params: dict[str, Any] | None,
178
+ *,
179
+ owner: str,
180
+ middleware_id: str,
181
+ ) -> dict[str, Any] | None:
182
+ if not params:
183
+ return None
184
+ if not params_model_path:
185
+ raise PzError(
186
+ code="PZ_MIDDLEWARE_PARAMS_MODEL_MISSING",
187
+ message=(
188
+ f"Middleware '{middleware_id}' declares params but does not declare a params_model."
189
+ ),
190
+ stage="middlewares",
191
+ owner=owner,
192
+ action="compile_middleware_manifest",
193
+ )
194
+ try:
195
+ module_name, _, attr_name = _normalize_import_path(params_model_path, owner=middleware_id).partition(":")
196
+ params_model = getattr(__import__(module_name, fromlist=[attr_name]), attr_name)
197
+ validated = params_model(**params)
198
+ return _normalize_validated_params(validated, params, middleware_id=middleware_id, owner=owner)
199
+ except PzError:
200
+ raise
201
+ except Exception as exc:
202
+ raise PzError(
203
+ code="PZ_MIDDLEWARE_PARAMS_INVALID",
204
+ message=f"Middleware '{middleware_id}' params failed validation: {exc}",
205
+ stage="middlewares",
206
+ owner=owner,
207
+ action="compile_middleware_manifest",
208
+ ) from exc
209
+
210
+
211
+ def _compile_decl(item: object, idx: int, *, owner_hint: str = "runtime") -> MiddlewareDecl | None:
212
+ if isinstance(item, MiddlewareDecl):
213
+ return item
214
+ if isinstance(item, str):
215
+ factory = item.strip()
216
+ owner = owner_hint
217
+ mid = f"middleware_{idx}"
218
+ priority = 100
219
+ replace = None
220
+ params_model = None
221
+ params = None
222
+ elif isinstance(item, dict):
223
+ owner = _read_optional_string(
224
+ item.get("owner"),
225
+ default=owner_hint,
226
+ code="PZ_MIDDLEWARE_OWNER_INVALID",
227
+ message="Middleware owner must be a non-empty string when provided.",
228
+ owner="runtime.middlewares",
229
+ ) or owner_hint
230
+ mid = _read_optional_string(
231
+ item.get("id"),
232
+ default=f"middleware_{idx}",
233
+ code="PZ_MIDDLEWARE_ID_INVALID",
234
+ message="Middleware id must be a non-empty string when provided.",
235
+ owner=owner,
236
+ ) or f"middleware_{idx}"
237
+ priority = _read_priority(
238
+ item.get("priority"),
239
+ default=100,
240
+ code="PZ_MIDDLEWARE_PRIORITY_INVALID",
241
+ message=f"Middleware '{mid}' priority must be an integer when provided.",
242
+ owner=owner,
243
+ )
244
+ factory = _read_optional_string(
245
+ item.get("factory"),
246
+ default=None,
247
+ code="PZ_MIDDLEWARE_IMPORT_INVALID",
248
+ message=f"Middleware '{mid}' factory must be a non-empty string when provided.",
249
+ owner=owner,
250
+ )
251
+ replace = _read_optional_string(
252
+ item.get("replace"),
253
+ default=None,
254
+ code="PZ_MIDDLEWARE_REPLACE_INVALID",
255
+ message=f"Middleware '{mid}' replace target must be a non-empty string when provided.",
256
+ owner=owner,
257
+ )
258
+ params_model = _read_optional_string(
259
+ item.get("params_model"),
260
+ default=None,
261
+ code="PZ_MIDDLEWARE_PARAMS_MODEL_INVALID",
262
+ message=f"Middleware '{mid}' params_model must be a non-empty string when provided.",
263
+ owner=owner,
264
+ )
265
+ raw_params = item.get("params")
266
+ if raw_params in (None, {}):
267
+ params = None
268
+ elif not isinstance(raw_params, dict):
269
+ raise PzError(
270
+ code="PZ_MIDDLEWARE_PARAMS_INVALID",
271
+ message=f"Middleware '{mid}' params must be a table.",
272
+ stage="middlewares",
273
+ owner=owner,
274
+ action="compile_middleware_manifest",
275
+ )
276
+ else:
277
+ params = dict(raw_params)
278
+ else:
279
+ raise PzError(
280
+ code="PZ_MIDDLEWARE_ITEM_INVALID",
281
+ message="Each middleware declaration must be a string import path or a table.",
282
+ stage="middlewares",
283
+ owner="runtime.middlewares",
284
+ action="compile_middleware_manifest",
285
+ )
286
+ if not factory:
287
+ raise PzError(
288
+ code="PZ_MIDDLEWARE_IMPORT_INVALID",
289
+ message=f"Middleware '{mid}' must declare a non-empty import/factory path.",
290
+ stage="middlewares",
291
+ owner=owner,
292
+ action="compile_middleware_manifest",
293
+ )
294
+ factory = _normalize_import_path(factory, owner=mid)
295
+ params = _validate_params_model(params_model, params, owner=owner, middleware_id=mid)
296
+ return MiddlewareDecl(
297
+ id=mid,
298
+ factory=factory,
299
+ owner=owner,
300
+ priority=priority,
301
+ replace=replace,
302
+ params_model=params_model,
303
+ params=params,
304
+ )
305
+
306
+
307
+ def _load_app_declared_middlewares(
308
+ settings: SettingsSnapshot,
309
+ app_registry: AppRegistry,
310
+ ) -> tuple[object, ...]:
311
+ out: list[object] = []
312
+ for app in app_registry.apps:
313
+ static_decl = read_static_app_declaration(
314
+ app.module_path,
315
+ action="compile_middleware_manifest",
316
+ )
317
+ if "middlewares" not in static_decl.method_names:
318
+ continue
319
+ app_obj = load_app_entrypoint_object_from_decl(app)
320
+ middlewares_factory = getattr(app_obj, "middlewares", None)
321
+ if middlewares_factory is None:
322
+ continue
323
+ if not callable(middlewares_factory):
324
+ raise PzError(
325
+ code="PZ_APP_MIDDLEWARES_INVALID",
326
+ message=f"App '{app.app_id}' exposes non-callable middlewares().",
327
+ stage="middlewares",
328
+ owner=app.app_id,
329
+ action="compile_middleware_manifest",
330
+ )
331
+ signature = inspect.signature(middlewares_factory)
332
+ if len(signature.parameters) == 0:
333
+ declared = middlewares_factory()
334
+ else:
335
+ declared = middlewares_factory(settings)
336
+ if declared in (None, (), []):
337
+ continue
338
+ if not isinstance(declared, (list, tuple)):
339
+ raise PzError(
340
+ code="PZ_APP_MIDDLEWARES_INVALID",
341
+ message=f"App '{app.app_id}' middlewares() must return a list or tuple.",
342
+ stage="middlewares",
343
+ owner=app.app_id,
344
+ action="compile_middleware_manifest",
345
+ )
346
+ for item in declared:
347
+ if not isinstance(item, MiddlewareDecl):
348
+ raise PzError(
349
+ code="PZ_APP_MIDDLEWARE_DECL_INVALID",
350
+ message=(
351
+ f"App '{app.app_id}' middlewares() must return MiddlewareDecl objects. "
352
+ "Legacy string/table declarations are not accepted in app code."
353
+ ),
354
+ stage="middlewares",
355
+ owner=app.app_id,
356
+ action="compile_middleware_manifest",
357
+ )
358
+ out.append((app.app_id, item))
359
+ return tuple(out)
360
+
361
+
362
+ def sort_middleware_declarations(
363
+ declarations: tuple[tuple[str, object], ...],
364
+ ) -> MiddlewareManifest:
365
+ out: list[MiddlewareDecl] = []
366
+ active_ids: dict[str, int] = {}
367
+
368
+ for idx, (owner_hint, item) in enumerate(declarations):
369
+ decl = _compile_decl(item, idx, owner_hint=owner_hint)
370
+ if decl is None:
371
+ continue
372
+ if decl.replace:
373
+ replaced_idx = active_ids.get(decl.replace)
374
+ if replaced_idx is None:
375
+ raise PzError(
376
+ code="PZ_MIDDLEWARE_REPLACE_TARGET_MISSING",
377
+ message=(
378
+ f"Middleware '{decl.id}' cannot replace missing middleware "
379
+ f"'{decl.replace}'."
380
+ ),
381
+ stage="middlewares",
382
+ owner=decl.owner,
383
+ action="compile_middleware_manifest",
384
+ )
385
+ out.pop(replaced_idx)
386
+ active_ids = {item.id: current for current, item in enumerate(out)}
387
+
388
+ current_idx = active_ids.get(decl.id)
389
+ if current_idx is not None:
390
+ previous = out[current_idx]
391
+ raise PzError(
392
+ code="PZ_MIDDLEWARE_DUPLICATE_ID",
393
+ message=(
394
+ f"Middleware id '{decl.id}' is declared more than once "
395
+ f"by owners '{previous.owner}' and '{decl.owner}'."
396
+ ),
397
+ stage="middlewares",
398
+ owner=decl.owner,
399
+ action="compile_middleware_manifest",
400
+ )
401
+ out.append(decl)
402
+ active_ids[decl.id] = len(out) - 1
403
+ return MiddlewareManifest(middlewares=tuple(out))
404
+
405
+
406
+ def compile_conventional_middleware_manifest(
407
+ settings: SettingsSnapshot,
408
+ app_registry: AppRegistry,
409
+ ) -> MiddlewareManifest:
410
+ project_dir = getattr(settings, "project_dir", None)
411
+ import_roots = getattr(settings, "import_roots", ())
412
+ with active_project_import_root(
413
+ project_dir,
414
+ extra_import_roots=import_roots,
415
+ ):
416
+ owner_order = {app.app_id: idx for idx, app in enumerate(app_registry.apps)}
417
+ declarations = list(_load_app_declared_middlewares(settings, app_registry))
418
+ declarations.sort(
419
+ key=lambda item: (
420
+ int(100 if getattr(item[1], "priority", 100) is None else getattr(item[1], "priority", 100)),
421
+ owner_order.get(item[0], len(owner_order)),
422
+ str(getattr(item[1], "id", "") or ""),
423
+ )
424
+ )
425
+ return sort_middleware_declarations(tuple(declarations))
426
+
427
+
428
+ def merge_explicit_middleware_order(
429
+ existing_ids: tuple[str, ...] | list[str],
430
+ conventional_manifest: MiddlewareManifest,
431
+ ) -> MiddlewareManifest:
432
+ conventional_ids = tuple(item.id for item in conventional_manifest.middlewares)
433
+ conventional_set = set(conventional_ids)
434
+ existing: list[str] = []
435
+ seen_existing: set[str] = set()
436
+ for item in existing_ids:
437
+ candidate = str(item).strip()
438
+ if not candidate or candidate not in conventional_set or candidate in seen_existing:
439
+ continue
440
+ existing.append(candidate)
441
+ seen_existing.add(candidate)
442
+ existing = tuple(existing)
443
+ if not existing:
444
+ return conventional_manifest
445
+
446
+ by_id = {item.id: item for item in conventional_manifest.middlewares}
447
+ existing_set = set(existing)
448
+ ordered_ids = list(existing)
449
+ for index, candidate in enumerate(conventional_ids):
450
+ if candidate in existing_set:
451
+ continue
452
+ anchor: str | None = None
453
+ for following in conventional_ids[index + 1 :]:
454
+ if following in existing_set:
455
+ anchor = following
456
+ break
457
+ if anchor is None:
458
+ ordered_ids.append(candidate)
459
+ continue
460
+ ordered_ids.insert(ordered_ids.index(anchor), candidate)
461
+ return MiddlewareManifest(middlewares=tuple(by_id[item_id] for item_id in ordered_ids))
462
+
463
+
464
+ def compile_middleware_manifest(
465
+ settings: SettingsSnapshot,
466
+ app_registry: AppRegistry | None = None,
467
+ ) -> MiddlewareManifest:
468
+ raw_middlewares = settings.get("middlewares", default=[])
469
+ if not isinstance(raw_middlewares, list):
470
+ raise PzError(
471
+ code="PZ_MIDDLEWARES_CONFIG_INVALID",
472
+ message="middlewares must be declared as an array of middleware declarations.",
473
+ stage="middlewares",
474
+ owner="runtime.middlewares",
475
+ action="compile_middleware_manifest",
476
+ )
477
+
478
+ explicit_manifest = sort_middleware_declarations(tuple(("runtime", item) for item in raw_middlewares))
479
+ if app_registry is None:
480
+ return explicit_manifest
481
+
482
+ conventional_manifest = compile_conventional_middleware_manifest(settings, app_registry)
483
+ if not explicit_manifest.middlewares:
484
+ return conventional_manifest
485
+
486
+ explicit_by_id = {item.id: item for item in explicit_manifest.middlewares}
487
+ explicit_ids = tuple(item.id for item in explicit_manifest.middlewares)
488
+ explicit_set = set(explicit_ids)
489
+ merged_ids = list(explicit_ids)
490
+ conventional_ids = tuple(item.id for item in conventional_manifest.middlewares)
491
+ for index, candidate in enumerate(conventional_ids):
492
+ if candidate in explicit_set:
493
+ continue
494
+ anchor: str | None = None
495
+ for following in conventional_ids[index + 1 :]:
496
+ if following in explicit_set:
497
+ anchor = following
498
+ break
499
+ if anchor is None:
500
+ merged_ids.append(candidate)
501
+ continue
502
+ merged_ids.insert(merged_ids.index(anchor), candidate)
503
+ conventional_by_id = {item.id: item for item in conventional_manifest.middlewares}
504
+ merged: list[MiddlewareDecl] = [
505
+ explicit_by_id[item_id] if item_id in explicit_by_id else conventional_by_id[item_id]
506
+ for item_id in merged_ids
507
+ ]
508
+ return MiddlewareManifest(middlewares=tuple(merged))
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from .context import ProjectContext
4
+ from .compiler import compile_project_context
5
+ from .discovery import active_project_import_root, resolve_project_context
6
+
7
+ __all__ = ["ProjectContext", "active_project_import_root", "compile_project_context", "resolve_project_context"]
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pz_core.assembly.project.context import ProjectContext
5
+ from pz_core.assembly.project.discovery import resolve_project_context
6
+
7
+
8
+ def compile_project_context(project_dir: str | os.PathLike[str] | None = None) -> ProjectContext:
9
+ return resolve_project_context(project_dir=project_dir)
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass(frozen=True, slots=True)
8
+ class ProjectContext:
9
+ project_dir: Path
10
+ project_config_path: Path
11
+ env_file_path: Path
12
+ cache_dir: Path
13
+ runtime_dir: Path
14
+ asset_dir: Path
15
+
16
+
17
+ __all__ = ["ProjectContext"]