Move mode detection from Rsx_Mode to Rsx class

Simplify ajax batching to be mode-based instead of configurable

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-01-14 22:33:49 +00:00
parent c83cae5a92
commit 2489997e42
13 changed files with 217 additions and 234 deletions

View File

@@ -61,4 +61,3 @@ GATEKEEPER_SUBTITLE="This is a restricted development preview site. Please enter
SSR_FPC_ENABLED=true
LOG_BROWSER_ERRORS=false
AJAX_DISABLE_BATCHING=false

View File

@@ -3,7 +3,7 @@
namespace App\RSpade\Commands\Rsx;
use Illuminate\Console\Command;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
/**
* Set the application mode (development, debug, or production)
@@ -23,9 +23,9 @@ class Mode_Set_Command extends Command
// Normalize mode aliases
$normalized = match (strtolower($mode)) {
'dev', 'development' => Rsx_Mode::DEVELOPMENT,
'debug' => Rsx_Mode::DEBUG,
'prod', 'production' => Rsx_Mode::PRODUCTION,
'dev', 'development' => Rsx::MODE_DEVELOPMENT,
'debug' => Rsx::MODE_DEBUG,
'prod', 'production' => Rsx::MODE_PRODUCTION,
default => null,
};
@@ -54,7 +54,7 @@ class Mode_Set_Command extends Command
$this->_update_env_mode($normalized);
// Clear the cached mode so subsequent calls see the new value
Rsx_Mode::clear_cache();
Rsx::clear_mode_cache();
// Step 2: Clear all caches
$this->line(' [2/3] Clearing caches...');
@@ -68,7 +68,7 @@ class Mode_Set_Command extends Command
// Step 3: Build appropriate assets
$this->line(' [3/3] Building assets...');
if ($normalized === Rsx_Mode::DEVELOPMENT) {
if ($normalized === Rsx::MODE_DEVELOPMENT) {
// In development, pre-warm the bundle cache
// Explicitly pass RSX_MODE to ensure subprocess uses correct mode
passthru("RSX_MODE={$normalized} php artisan rsx:bundle:compile", $exit_code);
@@ -100,7 +100,7 @@ class Mode_Set_Command extends Command
{
$env_path = base_path('.env');
if (!file_exists($env_path)) {
return Rsx_Mode::DEVELOPMENT;
return Rsx::MODE_DEVELOPMENT;
}
$contents = file_get_contents($env_path);
@@ -108,7 +108,7 @@ class Mode_Set_Command extends Command
return trim($matches[1]);
}
return Rsx_Mode::DEVELOPMENT;
return Rsx::MODE_DEVELOPMENT;
}
/**

View File

@@ -5,7 +5,7 @@ namespace App\RSpade\Commands\Rsx;
use App\RSpade\Core\Bundle\BundleCompiler;
use App\RSpade\Core\Bundle\Minifier;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
use Exception;
use Illuminate\Console\Command;
@@ -25,9 +25,7 @@ class Prod_Build_Command extends Command
public function handle(): int
{
$mode = Rsx_Mode::get();
if ($mode === Rsx_Mode::DEVELOPMENT) {
if (Rsx::is_development()) {
$this->warn('Warning: Building production assets in development mode.');
$this->line('Assets will be built but will not be used until you switch modes:');
$this->line(' php artisan rsx:mode:set debug');
@@ -62,7 +60,7 @@ class Prod_Build_Command extends Command
$this->line('[2/3] Compiling bundles...');
// Force restart minify server to pick up any code changes
if (Rsx_Mode::is_production()) {
if (Rsx::is_production()) {
Minifier::force_restart();
}
@@ -157,7 +155,7 @@ class Prod_Build_Command extends Command
$this->newLine();
$this->info("[OK] Production build complete ({$elapsed}s)");
if ($mode === Rsx_Mode::DEVELOPMENT) {
if (Rsx::is_development()) {
$this->newLine();
$this->line('To use these assets, switch to production mode:');
$this->line(' php artisan rsx:mode:set production');

View File

@@ -13,7 +13,7 @@ use App\RSpade\Core\Bundle\Rsx_Asset_Bundle_Abstract;
use App\RSpade\Core\Bundle\Rsx_Module_Bundle_Abstract;
use App\RSpade\Core\Locks\RsxLocks;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
/**
* BundleCompiler - Compiles RSX bundles into JS and CSS files
@@ -124,10 +124,10 @@ class BundleCompiler
public function compile(string $bundle_class, array $options = []): array
{
$this->bundle_name = $this->_get_bundle_name($bundle_class);
$this->is_production = Rsx_Mode::is_production_like();
$this->is_production = Rsx::is_production();
$force_build = $options['force_build'] ?? false;
console_debug('BUNDLE', "Compiling {$this->bundle_name} (mode: " . Rsx_Mode::get() . ')');
console_debug('BUNDLE', "Compiling {$this->bundle_name} (mode: " . Rsx::get_mode() . ')');
// Step 1: In production-like modes, require pre-built bundles (unless force_build)
if ($this->is_production && !$force_build) {
@@ -443,7 +443,7 @@ class BundleCompiler
$bundle_dir = storage_path('rsx-build/bundles');
// Look for split vendor/app files (current output format)
// Future: support merged files when Rsx_Mode::should_merge_bundles()
// Future: support merged files when Manifest::_should_merge_bundles()
$vendor_js_pattern = "{$bundle_dir}/{$this->bundle_name}__vendor.*.js";
$app_js_pattern = "{$bundle_dir}/{$this->bundle_name}__app.*.js";
$vendor_css_pattern = "{$bundle_dir}/{$this->bundle_name}__vendor.*.css";
@@ -1951,8 +1951,8 @@ implode("\n", array_map(fn ($f) => ' - ' . str_replace(base_path() . '/', '',
$js_content = $this->_compile_js_files($js_files);
// Minify JS in production mode only (strips sourcemaps)
if (Rsx_Mode::is_production()) {
// Minify JS in strict production only (strips sourcemaps, debug keeps them)
if (Rsx::is_production() && !Rsx::is_debug()) {
$js_content = Minifier::minify_js($js_content, "{$this->bundle_name}__{$type}.js");
}
@@ -1970,8 +1970,8 @@ implode("\n", array_map(fn ($f) => ' - ' . str_replace(base_path() . '/', '',
$css_content = $this->_compile_css_files($css_files);
// Minify CSS in production mode only (strips sourcemaps)
if (Rsx_Mode::is_production()) {
// Minify CSS in strict production only (strips sourcemaps, debug keeps them)
if (Rsx::is_production() && !Rsx::is_debug()) {
$css_content = Minifier::minify_css($css_content, "{$this->bundle_name}__{$type}.css");
}

View File

@@ -6,7 +6,7 @@ use RuntimeException;
use App\RSpade\CodeQuality\RuntimeChecks\BundleErrors;
use App\RSpade\Core\Bundle\BundleCompiler;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
use App\RSpade\Core\Session\Session;
/**
@@ -128,7 +128,7 @@ abstract class Rsx_Bundle_Abstract
\App\RSpade\Core\Debug\Debugger::dump_console_debug_messages_to_html();
// In development mode, validate path coverage
if (Rsx_Mode::is_development()) {
if (Rsx::is_development()) {
static::__validate_path_coverage($bundle_class);
}
@@ -229,7 +229,7 @@ abstract class Rsx_Bundle_Abstract
}
// Expect vendor/app split files (merging not yet implemented)
// Future: check Rsx_Mode::should_merge_bundles() for combined files
// Future: check Manifest::_should_merge_bundles() for combined files
if (!isset($compiled['vendor_js_bundle_path']) &&
!isset($compiled['app_js_bundle_path']) &&
!isset($compiled['vendor_css_bundle_path']) &&
@@ -260,16 +260,14 @@ abstract class Rsx_Bundle_Abstract
}
// Add runtime data
$rsxapp_data['debug'] = Rsx_Mode::is_development();
$rsxapp_data['current_controller'] = \App\RSpade\Core\Rsx::get_current_controller();
$rsxapp_data['current_action'] = \App\RSpade\Core\Rsx::get_current_action();
$rsxapp_data['debug'] = Rsx::is_development();
$rsxapp_data['current_controller'] = Rsx::get_current_controller();
$rsxapp_data['current_action'] = Rsx::get_current_action();
$rsxapp_data['is_auth'] = Session::is_logged_in();
$rsxapp_data['is_spa'] = \App\RSpade\Core\Rsx::is_spa();
$rsxapp_data['is_spa'] = Rsx::is_spa();
// Only include ajax_disable_batching in development mode
if (Rsx_Mode::is_development()) {
$rsxapp_data['ajax_disable_batching'] = config('rsx.development.ajax_disable_batching', false);
}
// Enable ajax batching in debug/production modes, disable in development for easier debugging
$rsxapp_data['ajax_batching'] = !Rsx::is_development();
// Add current params (always set to reduce state variations)
$current_params = \App\RSpade\Core\Rsx::get_current_params();
@@ -301,7 +299,7 @@ abstract class Rsx_Bundle_Abstract
$rsxapp_data['user_timezone'] = \App\RSpade\Core\Time\Rsx_Time::get_user_timezone();
// Add console_debug config only in development mode
if (Rsx_Mode::should_include_debug_info()) {
if (Manifest::_should_include_debug_info()) {
$console_debug_config = config('rsx.console_debug', []);
// Build console_debug settings

View File

@@ -74,7 +74,8 @@ class Ajax {
/**
* Make an AJAX call to an RSX controller action
*
* All calls are automatically batched unless window.rsxapp.ajax_disable_batching is true.
* Calls are batched when window.rsxapp.ajax_batching is true (debug/production modes).
* In development mode, batching is disabled for easier debugging.
*
* @param {string|object|function} url - The Ajax URL (e.g., '/_ajax/Controller_Name/action_name') or an object/function with a .path property
* @param {object} params - Parameters to send to the action
@@ -98,12 +99,12 @@ class Ajax {
console.log('Ajax:', controller, action, params);
// Check if batching is disabled for debugging
// Batch calls in debug/production mode, direct calls in development mode
let promise;
if (window.rsxapp && window.rsxapp.ajax_disable_batching) {
promise = Ajax._call_direct(controller, action, params);
} else {
if (window.rsxapp && window.rsxapp.ajax_batching) {
promise = Ajax._call_batch(controller, action, params);
} else {
promise = Ajax._call_direct(controller, action, params);
}
// Track this promise for unhandled rejection detection

View File

@@ -25,7 +25,7 @@ use App\RSpade\Core\Manifest\_Manifest_PHP_Reflection_Helper;
use App\RSpade\Core\Manifest\_Manifest_Quality_Helper;
use App\RSpade\Core\Manifest\_Manifest_Reflection_Helper;
use App\RSpade\Core\Manifest\_Manifest_Scanner_Helper;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
/**
* Manifest - RSX File Discovery and Metadata Management System
@@ -680,7 +680,7 @@ class Manifest
// Check both the static flag and the environment variable (set before process starts)
// Also auto-allow for safe commands that don't need manifest (e.g., rsx:clean)
$force_build = self::$_force_build || env('RSX_FORCE_BUILD', false) || self::_is_safe_command();
if (Rsx_Mode::is_production_like() && !$force_build) {
if (Rsx::is_production() && !$force_build) {
if (!$loaded_cache) {
throw new \RuntimeException(
'Manifest not built for production mode. Run: php artisan rsx:prod:build'
@@ -839,6 +839,80 @@ class Manifest
static::$_needs_manifest_restart = true;
}
// =========================================================================
// Build Mode Semantic Helpers
// =========================================================================
/**
* Check if manifest/bundles should auto-rebuild on file changes
*
* Only in development mode.
*/
public static function _should_auto_rebuild(): bool
{
return \App\RSpade\Core\Rsx::is_development();
}
/**
* Check if JS/CSS should be minified
*
* In debug and production modes.
*/
public static function _should_minify(): bool
{
return \App\RSpade\Core\Rsx::is_production();
}
/**
* Check if JS/CSS should be merged into single files
*
* Only in strict production mode (not debug - keeps files separate for debugging).
*/
public static function _should_merge_bundles(): bool
{
return \App\RSpade\Core\Rsx::is_production() && !\App\RSpade\Core\Rsx::is_debug();
}
/**
* Check if inline sourcemaps should be included
*
* In development and debug modes (not strict production).
*/
public static function _should_inline_sourcemaps(): bool
{
return \App\RSpade\Core\Rsx::is_development() || \App\RSpade\Core\Rsx::is_debug();
}
/**
* Check if CDN assets should be cached locally and bundled
*
* Only in strict production mode.
*/
public static function _should_cache_cdn(): bool
{
return \App\RSpade\Core\Rsx::is_production() && !\App\RSpade\Core\Rsx::is_debug();
}
/**
* Check if console_debug() calls should be stripped from source
*
* Only in strict production mode.
*/
public static function _should_strip_console_debug(): bool
{
return \App\RSpade\Core\Rsx::is_production() && !\App\RSpade\Core\Rsx::is_debug();
}
/**
* Check if debug info should be included in window.rsxapp
*
* Only in development mode.
*/
public static function _should_include_debug_info(): bool
{
return \App\RSpade\Core\Rsx::is_development();
}
/**
* Normalize class name to simple name (strip namespace qualifiers)
*

View File

@@ -4,7 +4,7 @@ namespace App\RSpade\Core\Manifest;
use App\RSpade\Core\Kernels\ManifestKernel;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Mode\Rsx_Mode;
use App\RSpade\Core\Rsx;
/**
* _Manifest_Cache_Helper - Persistence, loading, and validation
@@ -138,8 +138,8 @@ class _Manifest_Cache_Helper
$php_content .= '// Files: ' . count(Manifest::$data['data']['files']) . "\n";
$php_content .= '// Hash: ' . Manifest::$data['hash'] . "\n\n";
// Use compact format in production mode, pretty format in dev/debug
if (Rsx_Mode::is_production()) {
// Use compact format in strict production only, pretty format in dev/debug
if (Rsx::is_production() && !Rsx::is_debug()) {
$php_content .= 'return ' . self::_compact_var_export(Manifest::$data) . ";\n";
} else {
$php_content .= 'return ' . var_export(Manifest::$data, true) . ";\n";

View File

@@ -1,182 +0,0 @@
<?php
namespace App\RSpade\Core\Mode;
/**
* Rsx_Mode - Centralized application mode detection
*
* RSX_MODE is the authoritative source of truth for application mode,
* taking precedence over Laravel's APP_ENV setting.
*
* Three modes:
* - development: Auto-rebuild, full debugging, sourcemaps
* - debug: Production optimizations with sourcemaps for debugging
* - production: Full optimization, minification, merging, CDN bundling
*
* @see php artisan rsx:man app_mode
*/
class Rsx_Mode
{
public const DEVELOPMENT = 'development';
public const DEBUG = 'debug';
public const PRODUCTION = 'production';
private static ?string $_cached_mode = null;
/**
* Get the current application mode
*
* @return string One of: development, debug, production
*/
public static function get(): string
{
if (self::$_cached_mode !== null) {
return self::$_cached_mode;
}
$mode = env('RSX_MODE', self::DEVELOPMENT);
// Normalize aliases
if ($mode === 'dev') {
$mode = self::DEVELOPMENT;
} elseif ($mode === 'prod') {
$mode = self::PRODUCTION;
}
// Validate
if (!in_array($mode, [self::DEVELOPMENT, self::DEBUG, self::PRODUCTION], true)) {
throw new \RuntimeException(
"Invalid RSX_MODE '{$mode}'. Must be: development, debug, or production"
);
}
self::$_cached_mode = $mode;
return $mode;
}
/**
* Check if running in development mode
*/
public static function is_development(): bool
{
return self::get() === self::DEVELOPMENT;
}
/**
* Check if running in debug mode
*/
public static function is_debug(): bool
{
return self::get() === self::DEBUG;
}
/**
* Check if running in production mode
*/
public static function is_production(): bool
{
return self::get() === self::PRODUCTION;
}
/**
* Check if running in a production-like mode (debug or production)
*
* Use this for checks that apply to both debug and production,
* such as requiring pre-built assets.
*/
public static function is_production_like(): bool
{
return self::get() !== self::DEVELOPMENT;
}
/**
* Check if manifest/bundles should auto-rebuild on file changes
*
* Only in development mode.
*/
public static function should_auto_rebuild(): bool
{
return self::is_development();
}
/**
* Check if JS/CSS should be minified
*
* In debug and production modes.
*/
public static function should_minify(): bool
{
return self::is_production_like();
}
/**
* Check if JS/CSS should be merged into single files
*
* Only in production mode (not debug - keeps files separate for debugging).
*/
public static function should_merge_bundles(): bool
{
return self::is_production();
}
/**
* Check if inline sourcemaps should be included
*
* In development and debug modes.
*/
public static function should_inline_sourcemaps(): bool
{
return !self::is_production();
}
/**
* Check if CDN assets should be cached locally and bundled
*
* Only in production mode.
*/
public static function should_cache_cdn(): bool
{
return self::is_production();
}
/**
* Check if console_debug() calls should be stripped from source
*
* Only in production mode.
*/
public static function should_strip_console_debug(): bool
{
return self::is_production();
}
/**
* Check if debug info should be included in window.rsxapp
*
* Only in development mode.
*/
public static function should_include_debug_info(): bool
{
return self::is_development();
}
/**
* Clear the cached mode (for use after .env changes)
*/
public static function clear_cache(): void
{
self::$_cached_mode = null;
}
/**
* Get human-readable mode label
*/
public static function get_label(): string
{
return match (self::get()) {
self::DEVELOPMENT => 'Development',
self::DEBUG => 'Debug',
self::PRODUCTION => 'Production',
};
}
}

View File

@@ -22,6 +22,16 @@ use App\RSpade\Core\Manifest\Manifest;
*/
class Rsx
{
// Application mode constants
public const MODE_DEVELOPMENT = 'development';
public const MODE_DEBUG = 'debug';
public const MODE_PRODUCTION = 'production';
/**
* Cached application mode
*/
private static ?string $_cached_mode = null;
/**
* Current controller being executed
* @var string|null
@@ -106,6 +116,89 @@ class Rsx
return static::$current_route_type === 'spa';
}
// =========================================================================
// Application Mode Detection
// =========================================================================
/**
* Get the current application mode
*
* @return string One of: development, debug, production
*/
public static function get_mode(): string
{
if (self::$_cached_mode !== null) {
return self::$_cached_mode;
}
$mode = env('RSX_MODE', self::MODE_DEVELOPMENT);
// Normalize aliases
if ($mode === 'dev') {
$mode = self::MODE_DEVELOPMENT;
} elseif ($mode === 'prod') {
$mode = self::MODE_PRODUCTION;
}
// Validate
if (!in_array($mode, [self::MODE_DEVELOPMENT, self::MODE_DEBUG, self::MODE_PRODUCTION], true)) {
throw new \RuntimeException(
"Invalid RSX_MODE '{$mode}'. Must be: development, debug, or production"
);
}
self::$_cached_mode = $mode;
return $mode;
}
/**
* Check if running in development mode
*/
public static function is_development(): bool
{
return self::get_mode() === self::MODE_DEVELOPMENT;
}
/**
* Check if running in debug mode
*/
public static function is_debug(): bool
{
return self::get_mode() === self::MODE_DEBUG;
}
/**
* Check if running in production mode (debug or production)
*
* Returns true for both debug and production modes.
* Use is_production() && !is_debug() for strictly production-only checks.
*/
public static function is_production(): bool
{
return self::get_mode() !== self::MODE_DEVELOPMENT;
}
/**
* Clear the cached mode (for use after .env changes)
*/
public static function clear_mode_cache(): void
{
self::$_cached_mode = null;
}
/**
* Get human-readable mode label
*/
public static function get_mode_label(): string
{
return match (self::get_mode()) {
self::MODE_DEVELOPMENT => 'Development',
self::MODE_DEBUG => 'Debug',
self::MODE_PRODUCTION => 'Production',
};
}
/**
* Clear the current controller and action tracking
*/

View File

@@ -176,9 +176,12 @@ WINDOW.RSXAPP IN PRODUCTION
window.rsxapp output:
- console_debug configuration
- ajax_disable_batching flag
- Other debug-only properties
The ajax_batching flag is always present but mode-dependent:
- development: false (direct calls for easier debugging)
- debug/production: true (batched calls for efficiency)
Core functionality (user, site, csrf, params, etc.) remains
available for application code.

View File

@@ -93,7 +93,11 @@ OBJECT STRUCTURE
console_debug Object. Console debug configuration.
Controls console_debug() output filtering.
ajax_disable_batching Boolean. When true, Ajax calls bypass batching.
Mode-Dependent:
ajax_batching Boolean. When true, Ajax calls are batched.
Set automatically: true in debug/production,
false in development for easier debugging.
Optional:

View File

@@ -348,11 +348,6 @@ return [
// Show route matching details in error pages
'show_route_details' => env('RSX_SHOW_ROUTE_DETAILS', env('APP_DEBUG', false)),
// Disable AJAX request batching for easier debugging
// When true, each Ajax.call() makes an immediate individual request
// When false (default), requests are batched using setTimeout(0)
'ajax_disable_batching' => env('AJAX_DISABLE_BATCHING', false),
],
/*