splent-cli 1.53.0__tar.gz → 1.55.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.53.0/src/splent_cli.egg-info → splent_cli-1.55.0}/PKG-INFO +1 -1
  2. {splent_cli-1.53.0 → splent_cli-1.55.0}/pyproject.toml +1 -1
  3. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/command/command_create.py +2 -2
  4. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_frontend_init.py +10 -6
  5. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hook_add.py +7 -6
  6. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hook_remove.py +10 -6
  7. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hooks.py +1 -1
  8. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_npm.py +1 -1
  9. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pip_install.py +198 -3
  10. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_commands.py +7 -1
  11. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_frontend_audit.py +9 -5
  12. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_test.py +22 -18
  13. splent_cli-1.55.0/src/splent_cli/commands/product/product_ui_audit.py +233 -0
  14. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/own_commands.py +12 -5
  15. splent_cli-1.55.0/src/splent_cli/services/ui_audit.py +483 -0
  16. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +108 -45
  17. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +1 -1
  18. splent_cli-1.55.0/src/splent_cli/templates/ui_audit/measure.js +226 -0
  19. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/legacy.js +1 -1
  20. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/command_loader.py +10 -0
  21. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/frontend_check.py +1 -1
  22. {splent_cli-1.53.0 → splent_cli-1.55.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
  23. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/SOURCES.txt +3 -0
  24. {splent_cli-1.53.0 → splent_cli-1.55.0}/COPYING +0 -0
  25. {splent_cli-1.53.0 → splent_cli-1.55.0}/COPYING.LESSER +0 -0
  26. {splent_cli-1.53.0 → splent_cli-1.55.0}/README.md +0 -0
  27. {splent_cli-1.53.0 → splent_cli-1.55.0}/setup.cfg +0 -0
  28. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/__init__.py +0 -0
  29. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/__main__.py +0 -0
  30. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/cli.py +0 -0
  31. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/__init__.py +0 -0
  32. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  33. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  34. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  35. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  36. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  37. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  38. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  39. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  40. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  41. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/__init__.py +0 -0
  42. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
  43. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  44. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  45. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_env.py +0 -0
  46. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_features.py +0 -0
  47. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_github.py +0 -0
  48. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_infra.py +0 -0
  49. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_product.py +0 -0
  50. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  51. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  52. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_releases.py +0 -0
  53. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_vendor.py +0 -0
  54. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  55. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  56. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  57. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  58. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/command/__init__.py +0 -0
  59. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/coverage.py +0 -0
  60. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_console.py +0 -0
  61. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  62. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
  63. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_reset.py +0 -0
  64. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  65. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  66. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  67. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_status.py +0 -0
  68. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  69. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/doctor.py +0 -0
  70. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_list.py +0 -0
  71. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_set.py +0 -0
  72. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_show.py +0 -0
  73. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/export/__init__.py +0 -0
  74. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/export/export_puml.py +0 -0
  75. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  76. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  77. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  78. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  79. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  80. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  81. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  82. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  83. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  84. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  85. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  86. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  87. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  88. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  89. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  90. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  91. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
  92. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  93. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
  94. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  95. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  96. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  97. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  98. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  99. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  100. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_release.py +0 -0
  101. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  102. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  103. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  104. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_security.py +0 -0
  105. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  106. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  107. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
  108. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  109. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  110. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  111. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  112. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  113. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/lint.py +0 -0
  114. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/locust.py +0 -0
  115. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/login.py +0 -0
  116. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
  117. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  118. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
  119. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  120. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/__init__.py +0 -0
  121. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  122. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_build.py +0 -0
  123. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  124. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_config.py +0 -0
  125. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_configure.py +0 -0
  126. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_console.py +0 -0
  127. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_containers.py +0 -0
  128. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_create.py +0 -0
  129. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
  130. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_derive.py +0 -0
  131. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  132. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_down.py +0 -0
  133. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  134. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_env.py +0 -0
  135. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_list.py +0 -0
  136. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  137. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  138. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_params.py +0 -0
  139. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_port.py +0 -0
  140. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_release.py +0 -0
  141. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  142. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  143. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  144. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_run.py +0 -0
  145. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_select.py +0 -0
  146. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_shell.py +0 -0
  147. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  148. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  149. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_up.py +0 -0
  150. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_validate.py +0 -0
  151. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/__init__.py +0 -0
  152. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/release_core.py +0 -0
  153. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/release_resume.py +0 -0
  154. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/selenium.py +0 -0
  155. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  156. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  157. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  158. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  159. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  160. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  161. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  162. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  163. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  164. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  165. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  166. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  167. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  168. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  169. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  170. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  171. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/storage/__init__.py +0 -0
  172. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/storage/storage_migrate.py +0 -0
  173. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/tokens_setup.py +0 -0
  174. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  175. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/commands/version.py +0 -0
  176. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/__init__.py +0 -0
  177. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/compose.py +0 -0
  178. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/context.py +0 -0
  179. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/credentials.py +0 -0
  180. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/deploy_bundle.py +0 -0
  181. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/editable_refresh.py +0 -0
  182. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/frontend.py +0 -0
  183. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/image_registry.py +0 -0
  184. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/index_refresh.py +0 -0
  185. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace.py +0 -0
  186. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace_api.py +0 -0
  187. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace_url.py +0 -0
  188. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/neutral_app.py +0 -0
  189. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/preflight.py +0 -0
  190. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/pypi_quota.py +0 -0
  191. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/registry.py +0 -0
  192. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/release.py +0 -0
  193. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/release_dry_run.py +0 -0
  194. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/release_gate.py +0 -0
  195. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/release_workspace.py +0 -0
  196. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/security_scan.py +0 -0
  197. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/source_mirror.py +0 -0
  198. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/spl_store.py +0 -0
  199. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/services/web_container.py +0 -0
  200. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_.gitignore.j2 +0 -0
  201. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
  202. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
  203. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
  204. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
  205. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
  206. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
  207. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
  208. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
  209. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
  210. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
  211. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
  212. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
  213. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
  214. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
  215. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
  216. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
  217. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
  218. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
  219. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
  220. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -0
  221. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -0
  222. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
  223. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
  224. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
  225. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
  226. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
  227. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
  228. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
  229. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
  230. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
  231. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
  232. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
  233. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
  234. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
  235. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
  236. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
  237. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/licenses/COPYING +0 -0
  238. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
  239. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
  240. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.env.prod.example.j2 +0 -0
  241. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
  242. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
  243. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +0 -0
  244. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
  245. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
  246. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
  247. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
  248. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
  249. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +0 -0
  250. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +0 -0
  251. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
  252. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_400.html +0 -0
  253. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_401.html +0 -0
  254. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_404.html +0 -0
  255. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_500.html +0 -0
  256. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
  257. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
  258. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
  259. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
  260. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_app.css +0 -0
  261. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_app.js +0 -0
  262. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_base_template.html +0 -0
  263. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
  264. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
  265. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
  266. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +0 -0
  267. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_dropzone.css +0 -0
  268. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
  269. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
  270. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
  271. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_favicon.svg +0 -0
  272. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
  273. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
  274. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_own.css +0 -0
  275. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
  276. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -0
  277. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
  278. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/Dockerfile.frontend +0 -0
  279. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/package-lock.json +0 -0
  280. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/package.json +0 -0
  281. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/build.js +0 -0
  282. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/lint.js +0 -0
  283. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/plugin.js +0 -0
  284. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/preset.js +0 -0
  285. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/singletons.json +0 -0
  286. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/__init__.py +0 -0
  287. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/archetype.py +0 -0
  288. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/cache_utils.py +0 -0
  289. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/ci_services.py +0 -0
  290. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/ci_workflow.py +0 -0
  291. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  292. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/db_client.py +0 -0
  293. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/db_utils.py +0 -0
  294. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/decorators.py +0 -0
  295. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/deploy_freshness.py +0 -0
  296. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
  297. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_installer.py +0 -0
  298. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_naming.py +0 -0
  299. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_params.py +0 -0
  300. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_requirements.py +0 -0
  301. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_resolution.py +0 -0
  302. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_utils.py +0 -0
  303. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/forge.py +0 -0
  304. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/frontend_licenses.py +0 -0
  305. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/git_url.py +0 -0
  306. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/integrity.py +0 -0
  307. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/io_utils.py +0 -0
  308. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/lifecycle.py +0 -0
  309. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/manifest.py +0 -0
  310. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/menu_keys.py +0 -0
  311. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/migration_lock.py +0 -0
  312. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/npm_semver.py +0 -0
  313. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/path_utils.py +0 -0
  314. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/proc.py +0 -0
  315. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/redact.py +0 -0
  316. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/refinement_facts.py +0 -0
  317. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/running_version.py +0 -0
  318. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/template_drift.py +0 -0
  319. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/toml_edit.py +0 -0
  320. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/upgrade_plan.py +0 -0
  321. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
  322. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli/utils/uvl_params.py +0 -0
  323. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  324. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  325. {splent_cli-1.53.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/requires.txt +0 -0
  326. {splent_cli-1.53.0 → splent_cli-1.55.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.53.0
3
+ Version: 1.55.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.53.0"
7
+ version = "1.55.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:
@@ -15,6 +15,13 @@ and because a private feature may deliberately never be published there.
15
15
  Without the fallback, one unpublished feature makes the whole product
16
16
  unbuildable even though its code is tagged and reachable.
17
17
 
18
+ Everything is installed by one pip run. Which channel serves each feature is
19
+ settled first, asking PyPI and the forge for all of them at once, and a tag is
20
+ named by the commit it points at: pip keeps the wheel it builds from a commit
21
+ in its cache, and the next build takes it from there instead of cloning and
22
+ building the feature again. Only when that run fails are the features
23
+ installed one at a time, which says which one failed and why.
24
+
18
25
  Three things about this are worth stating plainly rather than discovering:
19
26
 
20
27
  * only [tool.splent].features is read. features_dev and features_prod are
@@ -39,7 +46,9 @@ Example:
39
46
  """
40
47
 
41
48
  import os
49
+ import subprocess
42
50
  import sys
51
+ from concurrent.futures import ThreadPoolExecutor
43
52
 
44
53
  import click
45
54
  import tomllib
@@ -154,6 +163,180 @@ def _last_line(output: str) -> str:
154
163
  return _redacted(lines[-1]) if lines else "no output"
155
164
 
156
165
 
166
+ # How many PyPI and forge questions are asked at once.
167
+ PROBE_WORKERS = 16
168
+
169
+ # Seconds one question to PyPI or one ``git ls-remote`` may take.
170
+ PROBE_TIMEOUT = 20
171
+
172
+ # What a failed pip run is summed up with, counted from its end.
173
+ TAIL_LINES = 15
174
+
175
+
176
+ def _asks_pypi() -> bool:
177
+ """Whether pip installs from PyPI, so PyPI is the one to ask.
178
+
179
+ A PIP_INDEX_URL that points elsewhere (a mirror, a private index) serves
180
+ what PyPI does not say, so nothing is asked and the features are
181
+ installed one at a time, each trying its index first.
182
+ """
183
+ index = os.getenv("PIP_INDEX_URL", "").strip()
184
+ return not index or index.rstrip("/") in (
185
+ "https://pypi.org/simple",
186
+ "https://pypi.python.org/simple",
187
+ )
188
+
189
+
190
+ def _pypi_serves(dist: str, version: str) -> bool | None:
191
+ """Whether PyPI serves ``dist==version``, None when it could not be asked."""
192
+ import requests
193
+
194
+ url = f"https://pypi.org/pypi/{dist}/{version}/json"
195
+ for _attempt in range(2):
196
+ try:
197
+ response = requests.get(url, timeout=PROBE_TIMEOUT)
198
+ except requests.RequestException:
199
+ continue
200
+ if response.status_code == 200:
201
+ return True
202
+ if response.status_code == 404:
203
+ return False
204
+ return None
205
+
206
+
207
+ def _tag_commit(namespace: str, name: str, ref: str) -> tuple[str, str, str] | None:
208
+ """(real_url, display_url, commit) of the tag, from the first spelling of
209
+ the namespace that has it, or None.
210
+
211
+ Asked with ``git ls-remote``, so nothing is cloned. An annotated tag is
212
+ named by the commit it points at, not by the tag object.
213
+ """
214
+ env = {
215
+ **os.environ,
216
+ **forge.git_auth_env(),
217
+ "GIT_TERMINAL_PROMPT": "0",
218
+ "LC_ALL": "C",
219
+ }
220
+ for spelling in namespace_spellings(namespace):
221
+ real, display = https_url(spelling, name)
222
+ try:
223
+ result = subprocess.run(
224
+ ["git", "ls-remote", real, f"refs/tags/{ref}", f"refs/tags/{ref}^{{}}"],
225
+ capture_output=True,
226
+ text=True,
227
+ timeout=PROBE_TIMEOUT,
228
+ env=env,
229
+ )
230
+ except (OSError, subprocess.TimeoutExpired):
231
+ continue
232
+ if result.returncode != 0:
233
+ continue
234
+ refs = dict(
235
+ reversed(line.split("\t", 1))
236
+ for line in result.stdout.splitlines()
237
+ if "\t" in line
238
+ )
239
+ commit = refs.get(f"refs/tags/{ref}^{{}}") or refs.get(f"refs/tags/{ref}")
240
+ if commit:
241
+ return real, display, commit
242
+ return None
243
+
244
+
245
+ def _pip_install_many(specs: list[str]) -> tuple[bool, str]:
246
+ """One pip run for every requirement in ``specs``.
247
+
248
+ What pip says is shown as it says it, redacted line by line, so a long
249
+ build is never silent. Returns whether it succeeded and its last lines.
250
+ """
251
+ auth = forge.git_auth_env()
252
+ cmd = [sys.executable, "-m", "pip", "install", "--progress-bar", "off", *specs]
253
+ tail: list[str] = []
254
+ try:
255
+ process = subprocess.Popen(
256
+ cmd,
257
+ stdout=subprocess.PIPE,
258
+ stderr=subprocess.STDOUT,
259
+ text=True,
260
+ env={**os.environ, **auth} if auth else None,
261
+ )
262
+ except OSError as exc:
263
+ return False, str(exc)
264
+ assert process.stdout is not None
265
+ for line in process.stdout:
266
+ line = _redacted(line.rstrip("\n"))
267
+ click.echo(f" {line}")
268
+ tail = [*tail, line][-TAIL_LINES:]
269
+ return process.wait() == 0, "\n".join(tail)
270
+
271
+
272
+ def _requirement(entry, data, pypi_only, asks_pypi):
273
+ """What one pip run installs for ``entry``.
274
+
275
+ Returns (spec, display, from_git), or None when it cannot be settled
276
+ without trying (PyPI could not be asked, or its tag could not be found),
277
+ which leaves the entry to the one-at-a-time install.
278
+ """
279
+ namespace, name, version, ref = _parse_feature_entry(entry)
280
+ dist = feature_distribution(data, name)
281
+ if not version:
282
+ return dist, dist, None
283
+ spec = f"{dist}=={version}"
284
+ if pypi_only:
285
+ return spec, spec, None
286
+ if not (ref and namespace):
287
+ return spec, spec, None
288
+ if not asks_pypi:
289
+ return None
290
+ served = _pypi_serves(dist, version)
291
+ if served is None:
292
+ return None
293
+ if served:
294
+ return spec, spec, None
295
+ found = _tag_commit(namespace, name, ref)
296
+ if found is None:
297
+ return None
298
+ real, display, commit = found
299
+ return (
300
+ f"{dist} @ git+{real}@{commit}",
301
+ f"git+{display}@{ref} ({commit[:12]})",
302
+ f"{name} {ref}",
303
+ )
304
+
305
+
306
+ def _install_together(features, data, pypi_only) -> list[str] | None:
307
+ """Install every feature in one pip run.
308
+
309
+ Returns what came from a git tag, or None when the features have to be
310
+ installed one at a time: one of them could not be settled in advance, or
311
+ the run failed and the one-at-a-time install says which feature broke it.
312
+ """
313
+ asks_pypi = _asks_pypi()
314
+ with ThreadPoolExecutor(max_workers=PROBE_WORKERS) as pool:
315
+ plan = list(
316
+ pool.map(
317
+ lambda entry: _requirement(entry, data, pypi_only, asks_pypi),
318
+ features,
319
+ )
320
+ )
321
+ if any(item is None for item in plan):
322
+ return None
323
+
324
+ for _spec, display, from_git in plan:
325
+ click.echo(f" {'git ' if from_git else 'pypi'} {display}")
326
+ click.echo()
327
+ ok, tail = _pip_install_many([spec for spec, _display, _git in plan])
328
+ click.echo()
329
+ if not ok:
330
+ click.secho(
331
+ " Installing them together failed. Installing them one at a time, "
332
+ "to say which one:",
333
+ fg="yellow",
334
+ )
335
+ click.echo()
336
+ return None
337
+ return [from_git for _spec, _display, from_git in plan if from_git]
338
+
339
+
157
340
  @click.command(
158
341
  "feature:pip-install",
159
342
  short_help="Install the declared features from PyPI, or from their git tag.",
@@ -204,15 +387,22 @@ def feature_pip_install(pypi_only):
204
387
 
205
388
  click.echo(f" Installing {len(features)} feature(s)...\n")
206
389
 
390
+ unpinned = [
391
+ _parse_feature_entry(entry)[1]
392
+ for entry in features
393
+ if not _parse_feature_entry(entry)[2]
394
+ ]
395
+ together = _install_together(features, data, pypi_only)
396
+ if together is not None:
397
+ _summary(features, unpinned, together, [])
398
+ return
399
+
207
400
  failed = []
208
- unpinned = []
209
401
  from_git = []
210
402
  for entry in features:
211
403
  namespace, name, version, ref = _parse_feature_entry(entry)
212
404
  dist = feature_distribution(data, name)
213
405
  spec = f"{dist}=={version}" if version else dist
214
- if not version:
215
- unpinned.append(name)
216
406
 
217
407
  click.echo(f" installing {spec}")
218
408
 
@@ -250,6 +440,11 @@ def feature_pip_install(pypi_only):
250
440
  )
251
441
  failed.append(spec)
252
442
 
443
+ _summary(features, unpinned, from_git, failed)
444
+
445
+
446
+ def _summary(features, unpinned, from_git, failed) -> None:
447
+ """What was installed from where, and what failed (which exits 1)."""
253
448
  click.echo()
254
449
  if unpinned:
255
450
  click.secho(
@@ -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: