wasm-cli 1.5.0__tar.gz → 1.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. {wasm_cli-1.5.0/src/wasm_cli.egg-info → wasm_cli-1.6.0}/PKG-INFO +1 -1
  2. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/superpowers/plans/2026-08-13-wasm-competitive-parity-roadmap.md +11 -0
  3. wasm_cli-1.6.0/docs/superpowers/specs/2026-08-14-wasm-panel-design-v2.md +127 -0
  4. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/pyproject.toml +1 -1
  5. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/setup.py +1 -1
  6. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/__init__.py +1 -1
  7. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/backup_schedules.py +2 -1
  8. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/cron.py +2 -1
  9. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/deps.py +8 -5
  10. wasm_cli-1.6.0/src/wasm/web/pydantic_compat.py +98 -0
  11. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/app.css +406 -214
  12. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/panel.js +18 -15
  13. wasm_cli-1.6.0/src/wasm/web/static/vendor/fonts/geist-mono-latin-400-normal.woff2 +0 -0
  14. wasm_cli-1.6.0/src/wasm/web/static/vendor/fonts/geist-mono-latin-500-normal.woff2 +0 -0
  15. wasm_cli-1.6.0/src/wasm/web/static/vendor/fonts/geist-sans-latin-400-normal.woff2 +0 -0
  16. wasm_cli-1.6.0/src/wasm/web/static/vendor/fonts/geist-sans-latin-500-normal.woff2 +0 -0
  17. wasm_cli-1.6.0/src/wasm/web/static/vendor/fonts/geist-sans-latin-600-normal.woff2 +0 -0
  18. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/_macros.html +7 -13
  19. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/db_engine.html +2 -2
  20. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/deploy_rows.html +8 -5
  21. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_section.html +1 -1
  22. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/activity.html +2 -2
  23. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/app.html +10 -10
  24. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/backups.html +1 -1
  25. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/certificates.html +1 -1
  26. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/dashboard.html +8 -6
  27. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/databases.html +3 -3
  28. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/deployment_detail.html +2 -2
  29. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/settings.html +4 -4
  30. {wasm_cli-1.5.0 → wasm_cli-1.6.0/src/wasm_cli.egg-info}/PKG-INFO +1 -1
  31. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm_cli.egg-info/SOURCES.txt +7 -7
  32. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  33. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  34. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-sans-condensed-latin-500-normal.woff2 +0 -0
  35. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-sans-condensed-latin-600-normal.woff2 +0 -0
  36. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  37. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  38. wasm_cli-1.5.0/src/wasm/web/static/vendor/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  39. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/LICENSE +0 -0
  40. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/MANIFEST.in +0 -0
  41. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/README.md +0 -0
  42. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/MONITOR.md +0 -0
  43. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/OBS_SETUP.md +0 -0
  44. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/assets/logo.png +0 -0
  45. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/assets/logo_bg.png +0 -0
  46. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/superpowers/specs/2026-08-12-wasm-panel-design-direction.md +0 -0
  47. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/superpowers/specs/2026-08-12-wasm-v1-refactor-design.md +0 -0
  48. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/docs/superpowers/specs/2026-08-13-wasm-competitive-parity-design.md +0 -0
  49. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/man/wasm.1 +0 -0
  50. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/setup.cfg +0 -0
  51. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/__main__.py +0 -0
  52. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/__init__.py +0 -0
  53. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/app.py +0 -0
  54. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/__init__.py +0 -0
  55. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/backup.py +0 -0
  56. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/cert.py +0 -0
  57. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/config.py +0 -0
  58. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/db.py +0 -0
  59. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/env.py +0 -0
  60. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/health.py +0 -0
  61. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/monitor.py +0 -0
  62. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/service.py +0 -0
  63. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/setup.py +0 -0
  64. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/site.py +0 -0
  65. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/store.py +0 -0
  66. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/version.py +0 -0
  67. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/web.py +0 -0
  68. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/commands/webapp.py +0 -0
  69. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/interactive.py +0 -0
  70. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/panel_links.py +0 -0
  71. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/cli/prompts.py +0 -0
  72. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/completions/README.md +0 -0
  73. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/completions/_wasm +0 -0
  74. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/completions/wasm.bash +0 -0
  75. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/completions/wasm.fish +0 -0
  76. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/__init__.py +0 -0
  77. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/app_state.py +0 -0
  78. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/config.py +0 -0
  79. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/dependencies.py +0 -0
  80. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/exceptions.py +0 -0
  81. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/fs.py +0 -0
  82. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/logger.py +0 -0
  83. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/net.py +0 -0
  84. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/notifier.py +0 -0
  85. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/runner.py +0 -0
  86. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/store.py +0 -0
  87. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/totp.py +0 -0
  88. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/update_checker.py +0 -0
  89. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/core/utils.py +0 -0
  90. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/__init__.py +0 -0
  91. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/auto.py +0 -0
  92. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/base.py +0 -0
  93. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/docker_compose.py +0 -0
  94. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/__init__.py +0 -0
  95. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/env_manager.py +0 -0
  96. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/health.py +0 -0
  97. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/nginx_config.py +0 -0
  98. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/package_manager.py +0 -0
  99. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/path_resolver.py +0 -0
  100. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/preflight.py +0 -0
  101. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/prisma.py +0 -0
  102. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/registration.py +0 -0
  103. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/summary.py +0 -0
  104. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/turbo.py +0 -0
  105. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/helpers/workspace.py +0 -0
  106. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/interface.py +0 -0
  107. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/monorepo.py +0 -0
  108. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/nextjs.py +0 -0
  109. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/nodejs.py +0 -0
  110. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/pipeline.py +0 -0
  111. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/python.py +0 -0
  112. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/recorder.py +0 -0
  113. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/registry.py +0 -0
  114. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/static.py +0 -0
  115. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/deployers/vite.py +0 -0
  116. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/main.py +0 -0
  117. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/__init__.py +0 -0
  118. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/apache_manager.py +0 -0
  119. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/backup_manager.py +0 -0
  120. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/backup_scheduler.py +0 -0
  121. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/base_manager.py +0 -0
  122. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/cert_manager.py +0 -0
  123. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/cron_manager.py +0 -0
  124. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/__init__.py +0 -0
  125. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/base.py +0 -0
  126. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/mongodb.py +0 -0
  127. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/mysql.py +0 -0
  128. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/postgres.py +0 -0
  129. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/redis.py +0 -0
  130. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/database/registry.py +0 -0
  131. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/nginx_manager.py +0 -0
  132. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/service_manager.py +0 -0
  133. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/source_manager.py +0 -0
  134. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/managers/webserver.py +0 -0
  135. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/__init__.py +0 -0
  136. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/email_notifier.py +0 -0
  137. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/metrics.py +0 -0
  138. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/models.py +0 -0
  139. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/observation_store.py +0 -0
  140. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/process_monitor.py +0 -0
  141. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/signals.py +0 -0
  142. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/monitor/timeseries.py +0 -0
  143. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/__init__.py +0 -0
  144. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/apache/proxy.conf.j2 +0 -0
  145. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/apache/static.conf.j2 +0 -0
  146. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/nginx/advanced.conf.j2 +0 -0
  147. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/nginx/monorepo.conf.j2 +0 -0
  148. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/nginx/proxy.conf.j2 +0 -0
  149. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/nginx/static.conf.j2 +0 -0
  150. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/_escape.j2 +0 -0
  151. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/app.service.j2 +0 -0
  152. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/backup-service.j2 +0 -0
  153. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/backup-timer.j2 +0 -0
  154. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/cron-service.j2 +0 -0
  155. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/cron-timer.j2 +0 -0
  156. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/templates/systemd/docker-compose.service.j2 +0 -0
  157. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/__init__.py +0 -0
  158. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/domain.py +0 -0
  159. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/environment.py +0 -0
  160. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/names.py +0 -0
  161. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/port.py +0 -0
  162. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/source.py +0 -0
  163. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/validators/ssh.py +0 -0
  164. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/__init__.py +0 -0
  165. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/__init__.py +0 -0
  166. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/apps.py +0 -0
  167. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/auth.py +0 -0
  168. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/backups.py +0 -0
  169. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/certs.py +0 -0
  170. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/config.py +0 -0
  171. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/databases.py +0 -0
  172. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/hooks.py +0 -0
  173. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/jobs.py +0 -0
  174. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/metrics.py +0 -0
  175. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/monitor.py +0 -0
  176. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/router.py +0 -0
  177. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/services.py +0 -0
  178. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/sites.py +0 -0
  179. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/api/system.py +0 -0
  180. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/auth.py +0 -0
  181. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/events.py +0 -0
  182. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/jobs.py +0 -0
  183. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/metrics_collector.py +0 -0
  184. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/server.py +0 -0
  185. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/logo.png +0 -0
  186. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/logo.webp +0 -0
  187. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/htmx.min.js +0 -0
  188. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/qrcodegen.js +0 -0
  189. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/sse.js +0 -0
  190. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/uplot.min.css +0 -0
  191. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/uplot.min.js +0 -0
  192. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/xterm.css +0 -0
  193. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/static/vendor/xterm.js +0 -0
  194. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/base.html +0 -0
  195. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/api_tokens.html +0 -0
  196. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/app_charts.html +0 -0
  197. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/app_env.html +0 -0
  198. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/backup_schedule_section.html +0 -0
  199. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/cron_runs.html +0 -0
  200. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/cron_section.html +0 -0
  201. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/db_console.html +0 -0
  202. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/deploy_form_check.html +0 -0
  203. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/deploy_form_options.html +0 -0
  204. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/deploy_recent.html +0 -0
  205. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/deploy_rollback.html +0 -0
  206. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/infra_service_editor.html +0 -0
  207. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/infra_service_form.html +0 -0
  208. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/infra_site_editor.html +0 -0
  209. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/infra_site_form.html +0 -0
  210. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/machine.html +0 -0
  211. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/notif_channels.html +0 -0
  212. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/sessions.html +0 -0
  213. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_editor.html +0 -0
  214. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_backups.html +0 -0
  215. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_notifications.html +0 -0
  216. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_paths.html +0 -0
  217. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_ssl.html +0 -0
  218. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_web.html +0 -0
  219. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/settings_form_webserver.html +0 -0
  220. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/totp.html +0 -0
  221. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/fragments/webhook_section.html +0 -0
  222. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/login.html +0 -0
  223. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/cron.html +0 -0
  224. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/deploy.html +0 -0
  225. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/deployments.html +0 -0
  226. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/failure.html +0 -0
  227. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/missing.html +0 -0
  228. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/resources.html +0 -0
  229. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/service_editor.html +0 -0
  230. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/service_new.html +0 -0
  231. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/site_editor.html +0 -0
  232. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/templates/pages/site_new.html +0 -0
  233. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/__init__.py +0 -0
  234. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/app_charts.py +0 -0
  235. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/backup_schedules.py +0 -0
  236. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/context.py +0 -0
  237. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/cron.py +0 -0
  238. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/databases.py +0 -0
  239. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/deploy_form.py +0 -0
  240. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/deployments.py +0 -0
  241. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/infrastructure.py +0 -0
  242. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/machine.py +0 -0
  243. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/rendering.py +0 -0
  244. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/resources.py +0 -0
  245. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/router.py +0 -0
  246. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/views/settings_editor.py +0 -0
  247. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/websockets/__init__.py +0 -0
  248. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm/web/websockets/router.py +0 -0
  249. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm_cli.egg-info/dependency_links.txt +0 -0
  250. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm_cli.egg-info/entry_points.txt +0 -0
  251. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm_cli.egg-info/requires.txt +0 -0
  252. {wasm_cli-1.5.0 → wasm_cli-1.6.0}/src/wasm_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wasm-cli
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: Web App System Management - Deploy and manage web applications on Linux servers
5
5
  Author: Yago López Prado
6
6
  Author-email: Yago López Prado <yago.lopez.adeje@gmail.com>
@@ -516,3 +516,14 @@ La API entera ya existe (`api/databases.py`). UI con tabs por engine:
516
516
  web, preview deploys, multiusuario, one-click, multi-server) sigue vigente.
517
517
  Decisión de publicación: tags v1.3.0/v1.4.0/v1.5.0 sin crear; cuando el dueño quiera
518
518
  publicar, seguir el proceso del spec de release (tag + push dispara PyPI/OBS).
519
+ - 2026-08-14 (publicación): **v1.5.0 publicada** (solo ese tag; 1.3/1.4 quedan como
520
+ hitos internos — los números publicados no necesitan ser contiguos y esos árboles
521
+ tenían man pages sin regenerar). **Rota en Ubuntu 24.04 al arrancar el panel**:
522
+ noble empaqueta pydantic v1 y tres ficheros nuevos usaban `field_validator` (v2).
523
+ **v1.5.1 publicada con el fix**: `web/pydantic_compat.py` (puente v1/v2), guardias
524
+ AST en test_architecture contra API exclusiva de una major de pydantic, job CI
525
+ `pydantic-v1` (tests web con 1.10 pineado) y paso de release que importa la capa web
526
+ dentro del contenedor deb/rpm con las deps de la distro. Lección permanente: los
527
+ tests con deps de pip NO validan las deps de distro; toda API nueva de una librería
528
+ empaquetada debe contrastarse con la versión de noble/bookworm/Fedora (tabla en
529
+ CLAUDE.md) y el gate del contenedor es el chokepoint que lo garantiza.
@@ -0,0 +1,127 @@
1
+ # Panel WASM - Dirección de diseño v2
2
+
3
+ Fecha: 2026-08-14
4
+ Sustituye a: `2026-08-12-wasm-panel-design-direction.md` (el dueño rechazó esa dirección
5
+ tras verla en producción: "pobre, asimétrica, componentes horribles"). Las restricciones
6
+ técnicas de aquel documento siguen vigentes; la estética no.
7
+
8
+ ## El veredicto que origina esto
9
+
10
+ La v1 apostó por un instrumento austero: todo acromático, cero acento, tipografía
11
+ condensada, raíles de 3px. Intelectualmente coherente; en pantalla lee como wireframe.
12
+ El dueño construye una alternativa a Vercel/Coolify y exige que el panel aguante el
13
+ careo visual con esos productos. Esa es la nueva vara.
14
+
15
+ ## Dirección
16
+
17
+ **SaaS profesional de 2026**: neutros fríos limpios, un acento índigo con propósito,
18
+ tipografía Geist, tarjetas con elevación sutil, simetría por esqueleto común. El modo
19
+ oscuro fija la identidad (es donde vive un operador); el claro es igual de completo.
20
+
21
+ Lo que se conserva de la v1 porque es correcto: **el color semántico de estado**
22
+ (verde=corriendo, ámbar=en curso, rojo=fallo, gris=parado) como única fuente de color
23
+ además del acento; los errores del sistema verbatim en mono; los vacíos como invitación;
24
+ todo vendorizado, cero CDN, cero build step, CSP intacta.
25
+
26
+ ## Tokens
27
+
28
+ ### Color
29
+
30
+ El acento vive en el espacio azul-violeta, que el sistema de estados deja libre.
31
+
32
+ | Rol | Claro | Oscuro |
33
+ |---|---|---|
34
+ | Fondo | `#FAFAFA` | `#0B0C0E` |
35
+ | Fondo hundido | `#F4F4F5` | `#08090A` |
36
+ | Superficie | `#FFFFFF` | `#141519` |
37
+ | Superficie elevada | `#FFFFFF` + sombra | `#1A1C21` |
38
+ | Borde | `#E4E4E7` | `#26282E` |
39
+ | Borde fuerte | `#D4D4D8` | `#363940` |
40
+ | Texto | `#18181B` | `#EDEEF0` |
41
+ | Texto atenuado | `#52525B` | `#A1A5AD` |
42
+ | Texto débil | `#71717A` | `#7D828B` |
43
+ | **Acento** | `#5B5BD6` | `#7C7CF0` |
44
+ | Acento hover | `#4F4FC4` | `#8F8FF5` |
45
+ | Acento tenue (fondos) | `#EEEEFB` | `#232345` |
46
+ | Activo | `#16A34A` | `#4ADE80` |
47
+ | Activo fondo | `#EAF7EF` | `#132E1D` |
48
+ | Fallo | `#DC2626` | `#F87171` |
49
+ | Fallo fondo | `#FDECEC` | `#341518` |
50
+ | En curso | `#D97706` | `#FBBF24` |
51
+ | En curso fondo | `#FDF3E4` | `#33270F` |
52
+ | Parado | `#71717A` | `#8B8F98` |
53
+ | Parado fondo | `#F1F1F2` | `#222329` |
54
+
55
+ Todos los pares texto/fondo y estado/fondo cumplen AA; el contrato de estilo lo
56
+ verifica computándolo, así que cualquier ajuste posterior queda vigilado.
57
+
58
+ ### Tipografía: Geist, dos voces
59
+
60
+ - **Geist Sans** (400/500/600) para todo el UI. Tracking ligeramente negativo en
61
+ títulos (−0.02em), pesos 600 para títulos, 500 para labels/botones, 400 para prosa.
62
+ - **Geist Mono** (400/500) para todo valor del sistema: rutas, units, puertos, logs,
63
+ timestamps, commits. Cifras tabulares en todo valor que cambie.
64
+ - Se retira IBM Plex entero, incluida la condensada (era la principal fuente de la
65
+ sensación "apretada"). Vendor: 5 woff2 de fontsource con checksum.
66
+
67
+ ### Espaciado, radios, elevación
68
+
69
+ - Escala: 4/8/12/16/24/32/48/64. Radio: 6px (controles), 10px (tarjetas), 999px
70
+ (pills). Contenedor de página: max-width 1200px centrado, gutter 24px (16px móvil).
71
+ - Sombras (solo claro; el oscuro eleva por color de superficie):
72
+ `--shadow-xs: 0 1px 2px rgb(0 0 0 / 5%)`,
73
+ `--shadow-sm: 0 1px 3px rgb(0 0 0 / 7%), 0 1px 2px rgb(0 0 0 / 5%)`.
74
+ - Transiciones 140ms ease-out en hover/focus; `prefers-reduced-motion` respetado.
75
+
76
+ ## Esqueleto común (la cura de la asimetría)
77
+
78
+ Toda página usa el mismo armazón, sin excepciones:
79
+
80
+ ```
81
+ ┌─ cockpit bar (sticky, blur) ─────────────────────────────┐
82
+ ├─ sidebar ─┬─ page ────────────────────────────────────────┤
83
+ │ │ header: h1 + descripción [acción 1ª] │
84
+ │ │ ── secciones con el mismo gap vertical ── │
85
+ │ │ card / tabla / form │
86
+ └───────────┴───────────────────────────────────────────────┘
87
+ ```
88
+
89
+ - Cabecera de página: título (xl/600) + una línea de descripción atenuada a la
90
+ izquierda; la acción primaria de la página a la derecha. Siempre.
91
+ - Secciones: título de sección (base/600) + gap uniforme (32px entre secciones,
92
+ 16px título→contenido). Nada de espaciados ad-hoc por plantilla.
93
+ - Tarjetas: superficie + borde + radio 10 + padding 20/24; en claro además sombra xs.
94
+ - Tablas: cabecera en texto atenuado 500 (12px, sin condensada, sin mayúsculas
95
+ forzadas salvo eyebrows), filas 44px, hover superficie, divisores internos solo
96
+ horizontales, celdas numéricas alineadas a la derecha en tabular.
97
+ - Estado: pill con punto de color (`● Running`) sobre fondo tenue del estado.
98
+ El raíl de 3px desaparece.
99
+
100
+ ## Componentes
101
+
102
+ - **Botones**: primario (acento, texto blanco, sombra xs), secundario (superficie +
103
+ borde), fantasma (sin borde, hover superficie), peligro (rojo solo como primario de
104
+ confirmación destructiva). Alturas 32/36, padding 12/16, radio 6, peso 500.
105
+ - **Formularios**: campos 36px, borde 1px, focus ring de 3px en acento tenue + borde
106
+ acento; labels 500 encima; hints atenuados debajo; errores en rojo con el mensaje
107
+ del sistema verbatim en mono dentro de un bloque, no inline chillón.
108
+ - **Cockpit bar** (la firma): sticky, fondo translúcido con `backdrop-filter: blur`,
109
+ hostname en mono + pill de estado global + sparkline de carga + memoria/disco como
110
+ micro-medidores + accesos (⌘K, tema). Es la única pieza con licencia para ser
111
+ distintiva; todo lo demás, disciplinado.
112
+ - **Sidebar**: grupos con eyebrow (11px/500/atenuado), item activo con fondo acento
113
+ tenue + texto acento, counts como pills mínimas.
114
+ - **Gráficas uPlot**: línea acento para métricas neutras (CPU/red), estados solo
115
+ cuando significan estado; ejes en Geist Mono 10px; marcas de deploy como líneas
116
+ discontinuas de estado.
117
+ - **Drawer de logs**: cabecera con pestaña, fondo hundido, xterm con el tema nuevo.
118
+ - **Login**: tarjeta centrada, logo, un campo, botón primario — la primera impresión
119
+ del producto, tratada como tal.
120
+
121
+ ## Ejecución
122
+
123
+ Los nombres de clases y de variables CSS se conservan (los contratos de estilo y el
124
+ cliente vigilan por nombre); cambian los valores y el CSS de componentes. Los templates
125
+ solo se tocan para imponer el esqueleto común (cabeceras de página, secciones). Se
126
+ verifica con capturas reales en ambos temas revisadas por el implementador, más
127
+ `test_web_style_contract` (contraste AA recalculado) y `panel_browser_check` (22/22).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wasm-cli"
7
- version = "1.5.0"
7
+ version = "1.6.0"
8
8
  description = "Web App System Management - Deploy and manage web applications on Linux servers"
9
9
  readme = "README.md"
10
10
  license = { text = "WASM-NCSAL" }
@@ -9,7 +9,7 @@ from setuptools import setup, find_packages
9
9
 
10
10
  setup(
11
11
  name="wasm-cli",
12
- version="1.5.0",
12
+ version="1.6.0",
13
13
  description="Web App System Management - Deploy and manage web applications on Linux servers",
14
14
  author="Yago López Prado",
15
15
  author_email="yago.lopez.adeje@gmail.com",
@@ -15,7 +15,7 @@ from importlib.metadata import version as _installed_version
15
15
  #: as an OBS build directory. The single source of truth is the ``version``
16
16
  #: field of pyproject.toml; ``scripts/release.py`` keeps this literal and the
17
17
  #: distribution packaging files in step with it.
18
- _FALLBACK_VERSION = "1.5.0"
18
+ _FALLBACK_VERSION = "1.6.0"
19
19
 
20
20
  try:
21
21
  __version__ = _installed_version("wasm-cli")
@@ -29,7 +29,7 @@ import logging
29
29
  from typing import Annotated
30
30
 
31
31
  from fastapi import APIRouter, Depends, HTTPException
32
- from pydantic import BaseModel, Field, field_validator
32
+ from pydantic import BaseModel, Field
33
33
 
34
34
  from wasm.core.exceptions import BackupError
35
35
  from wasm.core.utils import domain_to_app_name
@@ -41,6 +41,7 @@ from wasm.managers.backup_scheduler import (
41
41
  )
42
42
  from wasm.web.api.auth import get_current_session
43
43
  from wasm.web.api.deps import WASMErrorRoute, strict_domain
44
+ from wasm.web.pydantic_compat import field_validator
44
45
 
45
46
  router = APIRouter(route_class=WASMErrorRoute)
46
47
 
@@ -28,13 +28,14 @@ import logging
28
28
  from typing import Annotated, Any
29
29
 
30
30
  from fastapi import APIRouter, Depends, HTTPException, Query
31
- from pydantic import BaseModel, Field, field_validator
31
+ from pydantic import BaseModel, Field
32
32
 
33
33
  from wasm.core.exceptions import ServiceError
34
34
  from wasm.managers.backup_scheduler import SCHEDULE_ALIASES
35
35
  from wasm.managers.cron_manager import CronJob, CronManager, validate_cron_calendar
36
36
  from wasm.web.api.auth import get_current_session
37
37
  from wasm.web.api.deps import WASMErrorRoute
38
+ from wasm.web.pydantic_compat import field_validator
38
39
 
39
40
  router = APIRouter(route_class=WASMErrorRoute)
40
41
 
@@ -46,6 +46,7 @@ from wasm.core.exceptions import (
46
46
  )
47
47
  from wasm.validators.domain import validate_domain
48
48
  from wasm.web.auth import SCOPE_RANK, ensure_scope, require_auth
49
+ from wasm.web.pydantic_compat import dump_model
49
50
 
50
51
  #: Status used for a WASMError with no more specific mapping. A manager that
51
52
  #: raises anything else is reporting that the operation failed on the server,
@@ -130,11 +131,13 @@ def error_response(exc: WASMError) -> JSONResponse:
130
131
  # hint, and the client should not have to know the difference.
131
132
  return JSONResponse(
132
133
  status_code=status_for(exc),
133
- content=ErrorResponse(
134
- detail=str(exc),
135
- hint=getattr(exc, "details", None) or None,
136
- error=type(exc).__name__,
137
- ).model_dump(),
134
+ content=dump_model(
135
+ ErrorResponse(
136
+ detail=str(exc),
137
+ hint=getattr(exc, "details", None) or None,
138
+ error=type(exc).__name__,
139
+ )
140
+ ),
138
141
  )
139
142
 
140
143
 
@@ -0,0 +1,98 @@
1
+ # Copyright (c) 2024-2026 Yago Lopez Prado
2
+ # Licensed under WASM-NCSAL 1.0 (Commercial use prohibited)
3
+ # https://github.com/Perkybeet/wasm/blob/main/LICENSE
4
+
5
+ """
6
+ One import surface for the pydantic names that differ between v1 and v2.
7
+
8
+ The panel does not choose its pydantic. pip and Fedora install pydantic 2,
9
+ but Ubuntu 24.04 and Debian 12 package ``python3-pydantic`` 1.10, and the
10
+ .deb depends on the distribution package. FastAPI 0.100+ runs on either
11
+ major version, so the rest of the web layer never notices - until a module
12
+ imports a name only one major version has. That is how v1.5.0 shipped:
13
+ ``from pydantic import field_validator`` imported cleanly under pip's
14
+ pydantic 2, and ``wasm web start`` died with ImportError on every Ubuntu
15
+ 24.04 install.
16
+
17
+ This module bridges exactly the names the web layer uses, nothing more. It
18
+ is not a copy of pydantic and must not grow into one: a name goes here only
19
+ when the two majors spell it differently, and it keeps v2's spelling with
20
+ v2's semantics on both. ``tests/test_architecture.py`` forbids importing the
21
+ version-specific names from pydantic anywhere else.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import TYPE_CHECKING, Any
27
+
28
+ import pydantic
29
+ from pydantic import BaseModel
30
+
31
+ #: True under pydantic 2. Version detection has to work on both majors, and
32
+ #: ``pydantic.VERSION`` is the one spelling they share.
33
+ PYDANTIC_V2 = int(pydantic.VERSION.split(".")[0]) >= 2
34
+
35
+ if TYPE_CHECKING or PYDANTIC_V2:
36
+ from pydantic import field_validator as field_validator
37
+ else:
38
+ from pydantic import validator as _v1_validator
39
+
40
+ def field_validator(
41
+ field: str, *fields: str, mode: str = "after", check_fields: bool | None = None
42
+ ) -> Any:
43
+ """
44
+ pydantic v2's ``field_validator``, expressed as a v1 ``validator``.
45
+
46
+ Only the surface the web layer uses is mapped: ``mode="before"``
47
+ becomes v1's ``pre=True`` and ``mode="after"`` its default, both of
48
+ which mean the same thing they mean in v2. ``allow_reuse`` is always
49
+ set because v1 keeps a global registry of validator names and would
50
+ otherwise refuse the module on a re-import, which is exactly what a
51
+ reloading server does.
52
+
53
+ The decorated function must take ``(cls, value)`` and nothing else:
54
+ v2's ``ValidationInfo`` does not exist in v1, so a validator that
55
+ needs more than the value cannot be bridged and must be refactored
56
+ not to.
57
+
58
+ Args:
59
+ field: Name of the field to validate.
60
+ *fields: Further field names, validated by the same function.
61
+ mode: ``"before"`` or ``"after"``, with v2's meaning. The other
62
+ v2 modes have no v1 equivalent and are refused.
63
+ check_fields: Passed through to v1's ``check_fields`` untouched.
64
+
65
+ Returns:
66
+ The decorator, exactly as v2's ``field_validator`` would return.
67
+
68
+ Raises:
69
+ TypeError: When ``mode`` names a v2 mode v1 cannot express.
70
+ """
71
+ if mode not in ("before", "after"):
72
+ raise TypeError(
73
+ f"field_validator mode {mode!r} has no pydantic v1 equivalent; "
74
+ "use 'before' or 'after'"
75
+ )
76
+ kwargs: dict[str, Any] = {"pre": mode == "before", "allow_reuse": True}
77
+ if check_fields is not None:
78
+ kwargs["check_fields"] = check_fields
79
+ return _v1_validator(field, *fields, **kwargs)
80
+
81
+
82
+ def dump_model(model: BaseModel) -> dict[str, Any]:
83
+ """
84
+ Serialise a model to a plain dict, whichever pydantic is installed.
85
+
86
+ v2 spells this ``model_dump()`` and v1 ``dict()``; each major warns or
87
+ fails on the other's spelling, so callers use this instead of either.
88
+
89
+ Args:
90
+ model: The model instance to serialise.
91
+
92
+ Returns:
93
+ The model's fields as a dictionary, as the installed pydantic builds
94
+ it.
95
+ """
96
+ if PYDANTIC_V2:
97
+ return model.model_dump()
98
+ return model.dict()