splent-cli 1.52.0__tar.gz → 1.54.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 (326) hide show
  1. {splent_cli-1.52.0/src/splent_cli.egg-info → splent_cli-1.54.0}/PKG-INFO +1 -1
  2. {splent_cli-1.52.0 → splent_cli-1.54.0}/pyproject.toml +1 -1
  3. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/command/command_create.py +2 -2
  4. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_frontend_init.py +10 -6
  5. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_hook_add.py +7 -6
  6. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_hook_remove.py +10 -6
  7. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_hooks.py +1 -1
  8. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_npm.py +1 -1
  9. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_commands.py +7 -1
  10. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_frontend_audit.py +9 -5
  11. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_test.py +22 -18
  12. splent_cli-1.54.0/src/splent_cli/commands/product/product_ui_audit.py +233 -0
  13. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/own_commands.py +12 -5
  14. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/release_workspace.py +43 -0
  15. splent_cli-1.54.0/src/splent_cli/services/ui_audit.py +483 -0
  16. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +1 -1
  17. splent_cli-1.54.0/src/splent_cli/templates/ui_audit/measure.js +226 -0
  18. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/legacy.js +1 -1
  19. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/command_loader.py +10 -0
  20. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/frontend_check.py +1 -1
  21. {splent_cli-1.52.0 → splent_cli-1.54.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
  22. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli.egg-info/SOURCES.txt +3 -0
  23. {splent_cli-1.52.0 → splent_cli-1.54.0}/COPYING +0 -0
  24. {splent_cli-1.52.0 → splent_cli-1.54.0}/COPYING.LESSER +0 -0
  25. {splent_cli-1.52.0 → splent_cli-1.54.0}/README.md +0 -0
  26. {splent_cli-1.52.0 → splent_cli-1.54.0}/setup.cfg +0 -0
  27. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/__init__.py +0 -0
  28. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/__main__.py +0 -0
  29. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/cli.py +0 -0
  30. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/__init__.py +0 -0
  31. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  32. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  33. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  34. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  35. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  36. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  37. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  38. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  39. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  40. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/__init__.py +0 -0
  41. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
  42. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  43. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  44. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_env.py +0 -0
  45. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_features.py +0 -0
  46. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_github.py +0 -0
  47. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_infra.py +0 -0
  48. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_product.py +0 -0
  49. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  50. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  51. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_releases.py +0 -0
  52. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/check/check_vendor.py +0 -0
  53. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  54. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  55. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  56. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  57. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/command/__init__.py +0 -0
  58. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/coverage.py +0 -0
  59. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_console.py +0 -0
  60. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  61. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
  62. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_reset.py +0 -0
  63. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  64. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  65. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  66. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_status.py +0 -0
  67. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  68. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/doctor.py +0 -0
  69. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/env/env_list.py +0 -0
  70. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/env/env_set.py +0 -0
  71. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/env/env_show.py +0 -0
  72. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/export/__init__.py +0 -0
  73. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/export/export_puml.py +0 -0
  74. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  75. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  76. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  77. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  78. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  79. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  80. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  81. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  82. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  83. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  84. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  85. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  86. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  87. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  88. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  89. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  90. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
  91. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  92. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
  93. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  94. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  95. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  96. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  97. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  98. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  99. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  100. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_release.py +0 -0
  101. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  102. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  103. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  104. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_security.py +0 -0
  105. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  106. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  107. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
  108. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  109. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  110. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  111. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  112. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  113. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/lint.py +0 -0
  114. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/locust.py +0 -0
  115. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/marketplace/login.py +0 -0
  116. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
  117. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  118. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
  119. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  120. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/__init__.py +0 -0
  121. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  122. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_build.py +0 -0
  123. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  124. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_config.py +0 -0
  125. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_configure.py +0 -0
  126. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_console.py +0 -0
  127. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_containers.py +0 -0
  128. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_create.py +0 -0
  129. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
  130. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_derive.py +0 -0
  131. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  132. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_down.py +0 -0
  133. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  134. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_env.py +0 -0
  135. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_list.py +0 -0
  136. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  137. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  138. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_params.py +0 -0
  139. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_port.py +0 -0
  140. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_release.py +0 -0
  141. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  142. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  143. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  144. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_run.py +0 -0
  145. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_select.py +0 -0
  146. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_shell.py +0 -0
  147. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  148. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  149. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_up.py +0 -0
  150. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/product/product_validate.py +0 -0
  151. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/release/__init__.py +0 -0
  152. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/release/release_core.py +0 -0
  153. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/release/release_resume.py +0 -0
  154. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/selenium.py +0 -0
  155. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  156. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  157. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  158. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  159. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  160. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  161. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  162. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  163. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  164. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  165. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  166. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  167. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  168. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  169. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  170. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  171. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/storage/__init__.py +0 -0
  172. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/storage/storage_migrate.py +0 -0
  173. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/tokens_setup.py +0 -0
  174. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  175. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/commands/version.py +0 -0
  176. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/__init__.py +0 -0
  177. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/compose.py +0 -0
  178. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/context.py +0 -0
  179. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/credentials.py +0 -0
  180. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/deploy_bundle.py +0 -0
  181. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/editable_refresh.py +0 -0
  182. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/frontend.py +0 -0
  183. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/image_registry.py +0 -0
  184. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/index_refresh.py +0 -0
  185. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/marketplace.py +0 -0
  186. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/marketplace_api.py +0 -0
  187. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/marketplace_url.py +0 -0
  188. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/neutral_app.py +0 -0
  189. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/preflight.py +0 -0
  190. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/pypi_quota.py +0 -0
  191. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/registry.py +0 -0
  192. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/release.py +0 -0
  193. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/release_dry_run.py +0 -0
  194. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/release_gate.py +0 -0
  195. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/security_scan.py +0 -0
  196. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/source_mirror.py +0 -0
  197. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/spl_store.py +0 -0
  198. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/services/web_container.py +0 -0
  199. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_.gitignore.j2 +0 -0
  200. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
  201. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
  202. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
  203. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
  204. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
  205. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
  206. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
  207. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
  208. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
  209. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
  210. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
  211. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
  212. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
  213. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
  214. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
  215. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
  216. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
  217. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
  218. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
  219. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -0
  220. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -0
  221. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
  222. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
  223. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
  224. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
  225. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
  226. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
  227. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
  228. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
  229. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
  230. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
  231. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
  232. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
  233. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
  234. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
  235. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
  236. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/licenses/COPYING +0 -0
  237. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
  238. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
  239. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_.env.prod.example.j2 +0 -0
  240. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
  241. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
  242. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +0 -0
  243. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
  244. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
  245. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
  246. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
  247. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
  248. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +0 -0
  249. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +0 -0
  250. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
  251. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_400.html +0 -0
  252. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_401.html +0 -0
  253. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_404.html +0 -0
  254. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_500.html +0 -0
  255. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
  256. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
  257. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
  258. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +0 -0
  259. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
  260. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_app.css +0 -0
  261. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_app.js +0 -0
  262. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_base_template.html +0 -0
  263. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
  264. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
  265. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
  266. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +0 -0
  267. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_dropzone.css +0 -0
  268. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
  269. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
  270. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
  271. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_favicon.svg +0 -0
  272. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
  273. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
  274. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_own.css +0 -0
  275. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
  276. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -0
  277. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
  278. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/Dockerfile.frontend +0 -0
  279. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/package-lock.json +0 -0
  280. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/package.json +0 -0
  281. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/build.js +0 -0
  282. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/lint.js +0 -0
  283. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/plugin.js +0 -0
  284. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/preset.js +0 -0
  285. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/toolchain/frontend/splent/singletons.json +0 -0
  286. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/__init__.py +0 -0
  287. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/archetype.py +0 -0
  288. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/cache_utils.py +0 -0
  289. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/ci_services.py +0 -0
  290. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/ci_workflow.py +0 -0
  291. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  292. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/db_client.py +0 -0
  293. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/db_utils.py +0 -0
  294. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/decorators.py +0 -0
  295. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/deploy_freshness.py +0 -0
  296. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
  297. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_installer.py +0 -0
  298. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_naming.py +0 -0
  299. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_params.py +0 -0
  300. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_requirements.py +0 -0
  301. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_resolution.py +0 -0
  302. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/feature_utils.py +0 -0
  303. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/forge.py +0 -0
  304. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/frontend_licenses.py +0 -0
  305. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/git_url.py +0 -0
  306. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/integrity.py +0 -0
  307. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/io_utils.py +0 -0
  308. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/lifecycle.py +0 -0
  309. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/manifest.py +0 -0
  310. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/menu_keys.py +0 -0
  311. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/migration_lock.py +0 -0
  312. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/npm_semver.py +0 -0
  313. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/path_utils.py +0 -0
  314. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/proc.py +0 -0
  315. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/redact.py +0 -0
  316. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/refinement_facts.py +0 -0
  317. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/running_version.py +0 -0
  318. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/template_drift.py +0 -0
  319. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/toml_edit.py +0 -0
  320. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/upgrade_plan.py +0 -0
  321. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
  322. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli/utils/uvl_params.py +0 -0
  323. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  324. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  325. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli.egg-info/requires.txt +0 -0
  326. {splent_cli-1.52.0 → splent_cli-1.54.0}/src/splent_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.52.0
3
+ Version: 1.54.0
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.52.0"
7
+ version = "1.54.0"
8
8
  description = "SPLENT-CLI is a CLI to be able to work on your development more easily."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -10,7 +10,7 @@ from splent_cli.utils.path_utils import PathUtils
10
10
  # Segments separated by ':', each starting with a letter. Dashes are allowed
11
11
  # in the Click name (feature:inject-config) and become '_' in the file name
12
12
  # and the Python identifier.
13
- _NAME_RE = re.compile(r"[a-z][a-z0-9_-]*(:[a-z][a-z0-9_-]*)*")
13
+ _NAME_RE = re.compile(r"[a-z][a-z0-9_-]*(:[a-z][a-z0-9_-]*)?")
14
14
 
15
15
  # Names the generated module already uses; a command function with one of
16
16
  # these names would shadow the import it relies on.
@@ -33,7 +33,7 @@ def _validate_name(name: str) -> tuple[str, str]:
33
33
  ):
34
34
  raise click.ClickException(
35
35
  f"Invalid command name '{name}'. Use lowercase letters, digits, "
36
- "'_' and '-', starting with a letter, with ':' between segments "
36
+ "'_' and '-', starting with a letter, and at most one ':' "
37
37
  "(for example db:dump-all). Python keywords and the names click, "
38
38
  "current_app and requires_app are not allowed."
39
39
  )
@@ -1,5 +1,5 @@
1
1
  """
2
- splent feature:frontend:init
2
+ splent feature:frontend-init
3
3
 
4
4
  Move a feature to frontend/ (SD-01): its bundle is then built by the SPLENT
5
5
  toolchain from frontend/package.json, against the feature's own
@@ -17,6 +17,7 @@ from splent_cli.services import frontend
17
17
  from splent_cli.commands.feature.feature_npm import editable_frontend_root
18
18
  from splent_cli.utils.io_utils import load_toml
19
19
  from splent_cli.utils.template_drift import feature_ctx, render_template
20
+ from splent_cli.utils.command_loader import old_name
20
21
 
21
22
  # The SPLENT-owned files of a feature with frontend/, written as
22
23
  # feature:sync-template would write them.
@@ -176,7 +177,7 @@ def _leftovers(
176
177
 
177
178
 
178
179
  @click.command(
179
- "feature:frontend:init",
180
+ "feature:frontend-init",
180
181
  short_help="Give a feature a frontend/ of its own, built by the SPLENT toolchain.",
181
182
  )
182
183
  @click.argument("feature_ref")
@@ -208,8 +209,8 @@ def feature_frontend_init(feature_ref, adopt_vendor):
208
209
 
209
210
  \b
210
211
  Examples:
211
- splent feature:frontend:init charts
212
- splent feature:frontend:init charts --adopt-vendor
212
+ splent feature:frontend-init charts
213
+ splent feature:frontend-init charts --adopt-vendor
213
214
  """
214
215
  root, name = editable_frontend_root(feature_ref)
215
216
  if frontend.declares(root):
@@ -247,7 +248,7 @@ def feature_frontend_init(feature_ref, adopt_vendor):
247
248
  if fallback:
248
249
  (root / "frontend" / "webpack.extra.js").write_text(
249
250
  "// What the feature's webpack.config.js set beyond the SPLENT preset,\n"
250
- "// merged over it (feature:frontend:init carried it over).\n"
251
+ "// merged over it (feature:frontend-init carried it over).\n"
251
252
  "module.exports = {\n"
252
253
  f" resolve: {{ fallback: {json.dumps(fallback)} }},\n"
253
254
  "};\n",
@@ -308,4 +309,7 @@ def feature_frontend_init(feature_ref, adopt_vendor):
308
309
  click.echo()
309
310
 
310
311
 
311
- cli_command = feature_frontend_init
312
+ cli_commands = [
313
+ feature_frontend_init,
314
+ old_name("feature:frontend:init", feature_frontend_init),
315
+ ]
@@ -1,5 +1,5 @@
1
1
  """
2
- splent feature:hook:add
2
+ splent feature:hook-add
3
3
 
4
4
  Add a new template hook registration to a feature's hooks.py.
5
5
  Also updates [tool.splent.contract.provides].hooks in pyproject.toml.
@@ -17,13 +17,14 @@ from pathlib import Path
17
17
  from splent_cli.services import context
18
18
  from splent_cli.utils.feature_utils import normalize_namespace
19
19
  from splent_cli.utils.io_utils import atomic_write
20
+ from splent_cli.utils.command_loader import old_name
20
21
 
21
22
 
22
23
  DEFAULT_NAMESPACE = os.getenv("SPLENT_DEFAULT_NAMESPACE", "splent_io")
23
24
 
24
25
 
25
26
  # ─────────────────────────────────────────────────────────────────────────────
26
- # Helpers (shared with feature:hook:remove)
27
+ # Helpers (shared with feature:hook-remove)
27
28
  # ─────────────────────────────────────────────────────────────────────────────
28
29
 
29
30
 
@@ -317,7 +318,7 @@ def _check_function_name(function_name: str) -> None:
317
318
 
318
319
 
319
320
  @click.command(
320
- "feature:hook:add",
321
+ "feature:hook-add",
321
322
  short_help="Add a new template hook to a feature's hooks.py.",
322
323
  )
323
324
  @click.argument("feature_ref")
@@ -342,14 +343,14 @@ def feature_hook_add(feature_ref, slot, function_name):
342
343
 
343
344
  \b
344
345
  Example:
345
- splent feature:hook:add splent_feature_notes layout.sidebar render_notes_sidebar
346
+ splent feature:hook-add splent_feature_notes layout.sidebar render_notes_sidebar
346
347
  """
347
348
  _check_slot(slot)
348
349
  _check_function_name(function_name)
349
350
 
350
351
  workspace = str(context.workspace())
351
352
  feature_path, ns, name = resolve_editable(
352
- feature_ref, workspace, "feature:hook:add"
353
+ feature_ref, workspace, "feature:hook-add"
353
354
  )
354
355
 
355
356
  package_dir = feature_path / "src" / ns / name
@@ -403,4 +404,4 @@ def feature_hook_add(feature_ref, slot, function_name):
403
404
  click.echo()
404
405
 
405
406
 
406
- cli_command = feature_hook_add
407
+ cli_commands = [feature_hook_add, old_name("feature:hook:add", feature_hook_add)]
@@ -1,5 +1,5 @@
1
1
  """
2
- splent feature:hook:remove
2
+ splent feature:hook-remove
3
3
 
4
4
  Remove a template hook registration from a feature's hooks.py.
5
5
  Optionally also removes the callback function definition.
@@ -17,6 +17,7 @@ from splent_cli.commands.feature.feature_hook_add import (
17
17
  rewrite_provides_hooks,
18
18
  )
19
19
  from splent_cli.utils.io_utils import atomic_write
20
+ from splent_cli.utils.command_loader import old_name
20
21
 
21
22
 
22
23
  # ─────────────────────────────────────────────────────────────────────────────
@@ -90,7 +91,7 @@ def _remove_trailing_blank_lines(text: str) -> str:
90
91
 
91
92
 
92
93
  @click.command(
93
- "feature:hook:remove",
94
+ "feature:hook-remove",
94
95
  short_help="Remove a template hook from a feature's hooks.py.",
95
96
  )
96
97
  @click.argument("feature_ref")
@@ -116,12 +117,12 @@ def feature_hook_remove(feature_ref, slot, with_function, yes):
116
117
 
117
118
  \b
118
119
  Example:
119
- splent feature:hook:remove splent_feature_notes layout.sidebar
120
- splent feature:hook:remove splent_feature_notes layout.sidebar --with-function -y
120
+ splent feature:hook-remove splent_feature_notes layout.sidebar
121
+ splent feature:hook-remove splent_feature_notes layout.sidebar --with-function -y
121
122
  """
122
123
  workspace = str(context.workspace())
123
124
  feature_path, ns, name = resolve_editable(
124
- feature_ref, workspace, "feature:hook:remove"
125
+ feature_ref, workspace, "feature:hook-remove"
125
126
  )
126
127
 
127
128
  hooks_path = feature_path / "src" / ns / name / "hooks.py"
@@ -185,4 +186,7 @@ def feature_hook_remove(feature_ref, slot, with_function, yes):
185
186
  click.echo()
186
187
 
187
188
 
188
- cli_command = feature_hook_remove
189
+ cli_commands = [
190
+ feature_hook_remove,
191
+ old_name("feature:hook:remove", feature_hook_remove),
192
+ ]
@@ -138,7 +138,7 @@ def feature_hooks(feature_ref, as_json):
138
138
  click.echo()
139
139
  click.echo(
140
140
  click.style(" Manage hooks with:", fg="bright_black")
141
- + f" splent feature:hook:add {name} <slot> <function>"
141
+ + f" splent feature:hook-add {name} <slot> <function>"
142
142
  )
143
143
  click.echo()
144
144
 
@@ -68,7 +68,7 @@ def feature_npm(feature_ref, npm_args):
68
68
  if not frontend.declares(root):
69
69
  raise click.ClickException(
70
70
  f"{name} has no frontend/package.json. Create it with "
71
- f"splent feature:frontend:init {name}."
71
+ f"splent feature:frontend-init {name}."
72
72
  )
73
73
  args = [*npm_args, "--ignore-scripts"]
74
74
  if npm_args[0] in _SAVING:
@@ -80,7 +80,7 @@ def product_commands():
80
80
  own = _own_commands()
81
81
  product = context.active_app() or "product"
82
82
 
83
- if not registry and not own.commands and not own.shadowed and not own.error:
83
+ if not (registry or own.commands or own.shadowed or own.refused or own.error):
84
84
  click.echo()
85
85
  click.secho(" No feature or product commands registered.", fg="yellow")
86
86
  click.echo()
@@ -108,6 +108,12 @@ def product_commands():
108
108
  _table("Product Commands", "Product", own_rows)
109
109
  click.echo()
110
110
  click.secho(f" {len(own_rows)} command(s) of {product}'s own.", dim=True)
111
+ for name in own.refused:
112
+ click.secho(
113
+ f" ⚠ {product} has a command {name}, which cannot be reached: a "
114
+ "command's name has one colon. Use a hyphen.",
115
+ fg="yellow",
116
+ )
111
117
  for name in own.shadowed:
112
118
  click.secho(
113
119
  f" ⚠ {product} has a command {name}, which is the CLI's own and "
@@ -1,5 +1,5 @@
1
1
  """
2
- product:frontend:audit — what the product's bundles ship, audited as one set.
2
+ product:frontend-audit — what the product's bundles ship, audited as one set.
3
3
 
4
4
  Each feature with frontend/ audits its own lock when it is released (npm
5
5
  audit, services/frontend.py), but a bundle is immutable: an advisory
@@ -28,6 +28,7 @@ import click
28
28
  from splent_cli.services import compose, context, frontend
29
29
  from splent_cli.utils.frontend_check import LOCK_FILE, read_lock
30
30
  from splent_cli.utils.npm_semver import satisfies
31
+ from splent_cli.utils.command_loader import old_name
31
32
 
32
33
  DEFAULT_REGISTRY = "https://registry.npmjs.org"
33
34
  ADVISORIES = "/-/npm/v1/security/advisories/bulk"
@@ -206,7 +207,7 @@ def _vendored(workspace: str, feature: dict, name: str) -> list[dict]:
206
207
 
207
208
 
208
209
  @click.command(
209
- "product:frontend:audit",
210
+ "product:frontend-audit",
210
211
  short_help="Audit what the product's bundles ship against npm's advisories.",
211
212
  )
212
213
  @click.option(
@@ -234,8 +235,8 @@ def product_frontend_audit(sbom_path):
234
235
 
235
236
  \b
236
237
  Examples:
237
- splent product:frontend:audit
238
- splent product:frontend:audit --sbom frontend.sbom.cdx.json
238
+ splent product:frontend-audit
239
+ splent product:frontend-audit --sbom frontend.sbom.cdx.json
239
240
  """
240
241
  product = context.require_app()
241
242
  workspace = str(context.workspace())
@@ -320,4 +321,7 @@ def _product_version(product_dir: str) -> str:
320
321
  return str(data.get("project", {}).get("version") or "unknown")
321
322
 
322
323
 
323
- cli_command = product_frontend_audit
324
+ cli_commands = [
325
+ product_frontend_audit,
326
+ old_name("product:frontend:audit", product_frontend_audit),
327
+ ]
@@ -27,7 +27,7 @@ from pathlib import Path
27
27
 
28
28
  import click
29
29
 
30
- from splent_cli.services import context, web_container
30
+ from splent_cli.services import context, ui_audit, web_container
31
31
  from splent_cli.utils.feature_utils import (
32
32
  load_product_features,
33
33
  load_product_pyproject,
@@ -131,30 +131,34 @@ def _run_product_tests(product, workspace, levels, verbose):
131
131
 
132
132
  rc = run(cmd, check=False, env=env_vars).returncode
133
133
 
134
- if not own:
135
- return rc
136
-
137
134
  if levels:
138
135
  # A level flag asks for a quick, partial run. The product's tests
139
136
  # build its whole app and are not split by level, so they wait for
140
137
  # a full run, which is the one the release checks and the CI make.
138
+ if own:
139
+ click.secho(
140
+ f"\n {product}'s own tests ({OWN_TESTS_DIR}/) run only without a "
141
+ "level flag.",
142
+ dim=True,
143
+ )
144
+ return rc
145
+
146
+ if own:
141
147
  click.secho(
142
- f"\n {product}'s own tests ({OWN_TESTS_DIR}/) run only without a "
143
- "level flag.",
144
- dim=True,
148
+ f"\n ▶ {product} its own tests ({len(own)} file(s) in {OWN_TESTS_DIR}/)",
149
+ fg="cyan",
150
+ bold=True,
145
151
  )
146
- return rc
152
+ cmd = [sys.executable, "-m", "splent_cli", "product:test", "--own-tests"]
153
+ if verbose:
154
+ cmd.append("-v")
155
+ rc = run(cmd, check=False, env=env_vars).returncode or rc
147
156
 
148
- click.secho(
149
- f"\n ▶ {product} its own tests ({len(own)} file(s) in {OWN_TESTS_DIR}/)",
150
- fg="cyan",
151
- bold=True,
152
- )
153
- cmd = [sys.executable, "-m", "splent_cli", "product:test", "--own-tests"]
154
- if verbose:
155
- cmd.append("-v")
156
- own_rc = run(cmd, check=False, env=env_vars).returncode
157
- return rc or own_rc
157
+ # The look of its pages, when the product asks for it and its stack is up.
158
+ if ui_audit.declared(product_path):
159
+ cmd = [sys.executable, "-m", "splent_cli", "product:ui-audit", "--if-up"]
160
+ rc = run(cmd, check=False, env=env_vars).returncode or rc
161
+ return rc
158
162
 
159
163
 
160
164
  def _run_own_tests(product, verbose) -> None:
@@ -0,0 +1,233 @@
1
+ """
2
+ product:ui-audit — look at every page of the product in a real browser.
3
+
4
+ The checks live in services/ui_audit.py and the page itself, in
5
+ templates/ui_audit/measure.js. This is what runs them: in the product's web
6
+ container, the one place its Selenium grid can be reached from.
7
+ """
8
+
9
+ import os
10
+ import shutil
11
+
12
+ import click
13
+
14
+ from splent_cli.services import context, ui_audit, web_container
15
+
16
+
17
+ class _CannotRun(Exception):
18
+ """No browser or no app to look at, which says nothing about the pages."""
19
+
20
+
21
+ def _delegated_args(
22
+ url, start, widths, themes, public_only, screenshots, max_pages, if_up
23
+ ):
24
+ args = ["product:ui-audit"]
25
+ if if_up:
26
+ args.append("--if-up")
27
+ if url:
28
+ args += ["--url", url]
29
+ for path in start:
30
+ args += ["--start", path]
31
+ for width in widths:
32
+ args += ["--width", str(width)]
33
+ if len(themes) == 1:
34
+ args += ["--theme", themes[0]]
35
+ if public_only:
36
+ args.append("--public-only")
37
+ if screenshots:
38
+ args.append("--screenshots")
39
+ if max_pages != ui_audit.MAX_PAGES:
40
+ args += ["--max-pages", str(max_pages)]
41
+ return args
42
+
43
+
44
+ def render(results, out) -> int:
45
+ """Print what RESULTS found. Returns how many distinct problems."""
46
+ summary = ui_audit.summarize(results)
47
+ problems = 0
48
+ for kind, title in ui_audit.KINDS.items():
49
+ entries = summary.get(kind)
50
+ if not entries:
51
+ continue
52
+ problems += len(entries)
53
+ click.echo()
54
+ click.secho(f" {title} ({len(entries)})", bold=True)
55
+ ranked = sorted(entries.items(), key=lambda kv: -len(kv[1]["paths"]))
56
+ for key, entry in ranked:
57
+ click.echo(f" {key}")
58
+ pages = len(entry["paths"])
59
+ more = f" and {pages - 1} more" if pages > 1 else ""
60
+ detail = f" {entry['detail']}" if entry["detail"] else ""
61
+ click.secho(
62
+ f" [{ui_audit.where_label(entry['where'])}] "
63
+ f"{entry['paths'][0]}{more}{detail}",
64
+ dim=True,
65
+ )
66
+ failed = ui_audit.errors_of(results)
67
+ for record in failed[:5]:
68
+ click.secho(
69
+ f" could not measure {record['url']}: {record['error']}", fg="yellow"
70
+ )
71
+ click.echo()
72
+ pages = {(r.get("who"), r.get("path")) for r in results if "error" not in r}
73
+ click.echo(f" {len(pages)} pages measured, {len(results)} measurements.")
74
+ click.secho(f" Report and screenshots in {out}", dim=True)
75
+ return problems + len(failed)
76
+
77
+
78
+ @click.command(
79
+ "product:ui-audit",
80
+ short_help="Look at every page in a real browser, at every width and theme.",
81
+ )
82
+ @click.option(
83
+ "--url",
84
+ default=None,
85
+ help="Audit this address, such as your production site, as an anonymous "
86
+ "visitor. Defaults to the product's development app.",
87
+ )
88
+ @click.option("--start", multiple=True, help="Start from this path. Can be repeated.")
89
+ @click.option(
90
+ "--width",
91
+ "widths",
92
+ multiple=True,
93
+ type=int,
94
+ help="Only this width, in both themes. Can be repeated.",
95
+ )
96
+ @click.option(
97
+ "--theme",
98
+ type=click.Choice(["light", "dark", "both"]),
99
+ default="both",
100
+ help="Which theme to look at.",
101
+ )
102
+ @click.option("--public-only", is_flag=True, help="Skip the pages behind a login.")
103
+ @click.option(
104
+ "--screenshots",
105
+ is_flag=True,
106
+ help="Keep a screenshot of every page, not only of those with a problem.",
107
+ )
108
+ @click.option(
109
+ "--max-pages",
110
+ default=ui_audit.MAX_PAGES,
111
+ show_default=True,
112
+ help="Stop discovering pages after this many, per visitor.",
113
+ )
114
+ @click.option(
115
+ "--if-up",
116
+ is_flag=True,
117
+ hidden=True,
118
+ help="Pass, saying so, when there is no grid or no app to look at "
119
+ "(used by product:test, whose tests need neither).",
120
+ )
121
+ def product_ui_audit(
122
+ url, start, widths, theme, public_only, screenshots, max_pages, if_up
123
+ ):
124
+ """
125
+ Look at every page of the product in a real browser.
126
+
127
+ \b
128
+ Walks the links from the first page and, in Chrome, at five widths from
129
+ a wide screen to a small phone and in the light and dark themes, looks
130
+ for what the tests cannot see: what leaves the screen, is cut off, is
131
+ squeezed or sits under something else, text too faint to read, light
132
+ boxes left on a dark page, and pictures that fail or fade away.
133
+
134
+ \b
135
+ Fails when it finds anything, so it can stop a release. product:test
136
+ runs it too when the product has a [tool.splent.ui_audit] table.
137
+
138
+ \b
139
+ Examples:
140
+ splent product:ui-audit
141
+ splent product:ui-audit --width 390 --theme dark
142
+ splent product:ui-audit --url https://www.example.org --screenshots
143
+ """
144
+ product = context.require_app()
145
+ themes = ("light", "dark") if theme == "both" else (theme,)
146
+ try:
147
+ if not web_container.is_product_container():
148
+ container = web_container.name(product)
149
+ if not web_container.running(container):
150
+ raise _CannotRun(
151
+ f"{container} is not running. Start the product with "
152
+ "splent product:up, then splent product:run --dev"
153
+ )
154
+ raise SystemExit(
155
+ web_container.delegate(
156
+ product,
157
+ _delegated_args(
158
+ url,
159
+ start,
160
+ widths,
161
+ themes,
162
+ public_only,
163
+ screenshots,
164
+ max_pages,
165
+ if_up,
166
+ ),
167
+ )
168
+ )
169
+ audit, out = _audit_the_product(
170
+ product, url, start, widths, themes, public_only, screenshots, max_pages
171
+ )
172
+ except _CannotRun as error:
173
+ if not if_up:
174
+ raise click.ClickException(str(error)) from error
175
+ click.secho(f"\n The UI audit did not run. {error}", fg="yellow")
176
+ return
177
+ except ui_audit.AuditError as error:
178
+ raise click.ClickException(str(error)) from error
179
+
180
+ if render(audit.results, out):
181
+ click.secho(" The audit found problems.\n", fg="red")
182
+ raise SystemExit(1)
183
+ click.secho(" Nothing wrong.\n", fg="green")
184
+
185
+
186
+ def _audit_the_product(
187
+ product, url, start, widths, themes, public_only, screenshots, max_pages
188
+ ):
189
+ """Run the audit here, in the web container. Returns it and where it wrote."""
190
+ workspace = context.workspace()
191
+ config = ui_audit.read_config(workspace / product)
192
+ if start:
193
+ config.start = list(start)
194
+
195
+ grid_url = os.getenv("SELENIUM_GRID_URL")
196
+ grid = ui_audit.Grid(grid_url) if grid_url else None
197
+ if grid is None or not grid.ready():
198
+ raise _CannotRun(
199
+ "No Selenium grid answers here. It is part of the product's development "
200
+ "stack: start it with splent product:up"
201
+ )
202
+ base = url or os.getenv("SELENIUM_TARGET_URL")
203
+ if not base:
204
+ raise _CannotRun(
205
+ "SELENIUM_TARGET_URL is not set, so the browser does not know where the "
206
+ "app is. Pass --url"
207
+ )
208
+
209
+ out = workspace / ".splent_cache" / "ui_audit" / product
210
+ shutil.rmtree(out, ignore_errors=True)
211
+ audit = ui_audit.Audit(
212
+ grid,
213
+ base,
214
+ config,
215
+ out=out,
216
+ max_pages=max_pages,
217
+ log=lambda line: click.echo(line),
218
+ )
219
+ audit.every_screenshot = screenshots
220
+ runs = ui_audit.plan(widths, themes)
221
+
222
+ click.secho(f"\n Auditing {base}\n", bold=True)
223
+ try:
224
+ audit.measure_as("public", None, runs)
225
+ # A login is a test account of the development app, and only there.
226
+ if not url and not public_only:
227
+ for login in config.logins:
228
+ audit.measure_as(login.split(":", 1)[0].split("@")[0], login, runs)
229
+ except ui_audit.NothingAnswers as error:
230
+ raise _CannotRun(str(error)) from error
231
+ finally:
232
+ audit.write_report()
233
+ return audit, out
@@ -9,7 +9,9 @@ context.
9
9
 
10
10
  The CLI's own ``product:*`` commands win. A product command named like one
11
11
  of them cannot be reached, and product:commands says so: a CLI that ships a
12
- new built-in never takes a product's command away unnoticed.
12
+ new built-in never takes a product's command away unnoticed. A name with a
13
+ colon of its own would make two, which no command has, so it is left out and
14
+ product:commands says so too.
13
15
  """
14
16
 
15
17
  from __future__ import annotations
@@ -25,12 +27,14 @@ MODULE = "commands"
25
27
 
26
28
  class OwnCommands(NamedTuple):
27
29
  """What a product contributes: ``commands`` by the name they are run
28
- with, ``shadowed`` the names a built-in took, and ``error`` why its
29
- commands module could not be imported, None when it could."""
30
+ with, ``shadowed`` the names a built-in took, ``error`` why its commands
31
+ module could not be imported, None when it could, and ``refused`` the
32
+ names with a colon of their own."""
30
33
 
31
34
  commands: dict[str, click.Command]
32
35
  shadowed: list[str]
33
36
  error: str | None
37
+ refused: list[str] = []
34
38
 
35
39
 
36
40
  NONE = OwnCommands({}, [], None)
@@ -62,15 +66,18 @@ def collect(product: str | None, builtin) -> OwnCommands:
62
66
  commands = declared(product)
63
67
  except Exception as e:
64
68
  return OwnCommands({}, [], f"{product}.{MODULE}: {e}")
65
- found, shadowed = {}, []
69
+ found, shadowed, refused = {}, [], []
66
70
  for command in commands:
67
71
  if not command.name:
68
72
  continue
69
73
  name = PREFIX + command.name
74
+ if ":" in command.name:
75
+ refused.append(name)
76
+ continue
70
77
  if name in builtin:
71
78
  shadowed.append(name)
72
79
  continue
73
80
  # Run in the app's context, as a feature's (cli.py).
74
81
  command.requires_app = True # type: ignore[attr-defined]
75
82
  found[name] = command
76
- return OwnCommands(found, shadowed, None)
83
+ return OwnCommands(found, shadowed, None, refused)