diff --git a/.env.dist b/.env.dist index 386afd8e9..88b6fe0bd 100755 --- a/.env.dist +++ b/.env.dist @@ -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 diff --git a/app/RSpade/Commands/Rsx/Mode_Set_Command.php b/app/RSpade/Commands/Rsx/Mode_Set_Command.php index 304279446..89316c485 100755 --- a/app/RSpade/Commands/Rsx/Mode_Set_Command.php +++ b/app/RSpade/Commands/Rsx/Mode_Set_Command.php @@ -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; } /** diff --git a/app/RSpade/Commands/Rsx/Prod_Build_Command.php b/app/RSpade/Commands/Rsx/Prod_Build_Command.php index 1860eea08..a21ed4140 100755 --- a/app/RSpade/Commands/Rsx/Prod_Build_Command.php +++ b/app/RSpade/Commands/Rsx/Prod_Build_Command.php @@ -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'); diff --git a/app/RSpade/Core/Bundle/BundleCompiler.php b/app/RSpade/Core/Bundle/BundleCompiler.php index b8024decd..a8592a444 100644 --- a/app/RSpade/Core/Bundle/BundleCompiler.php +++ b/app/RSpade/Core/Bundle/BundleCompiler.php @@ -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"); } diff --git a/app/RSpade/Core/Bundle/Rsx_Bundle_Abstract.php b/app/RSpade/Core/Bundle/Rsx_Bundle_Abstract.php index 7d74f4b20..a7a38d94e 100644 --- a/app/RSpade/Core/Bundle/Rsx_Bundle_Abstract.php +++ b/app/RSpade/Core/Bundle/Rsx_Bundle_Abstract.php @@ -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 diff --git a/app/RSpade/Core/Js/Ajax.js b/app/RSpade/Core/Js/Ajax.js index 6ab0735c1..649fac331 100755 --- a/app/RSpade/Core/Js/Ajax.js +++ b/app/RSpade/Core/Js/Ajax.js @@ -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 diff --git a/app/RSpade/Core/Manifest/Manifest.php b/app/RSpade/Core/Manifest/Manifest.php index 82a4095a9..07cd19d7c 100644 --- a/app/RSpade/Core/Manifest/Manifest.php +++ b/app/RSpade/Core/Manifest/Manifest.php @@ -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) * diff --git a/app/RSpade/Core/Manifest/_Manifest_Cache_Helper.php b/app/RSpade/Core/Manifest/_Manifest_Cache_Helper.php index f07c8d068..aa23f5cc1 100755 --- a/app/RSpade/Core/Manifest/_Manifest_Cache_Helper.php +++ b/app/RSpade/Core/Manifest/_Manifest_Cache_Helper.php @@ -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"; diff --git a/app/RSpade/Core/Mode/Rsx_Mode.php b/app/RSpade/Core/Mode/Rsx_Mode.php deleted file mode 100755 index 49fe9f497..000000000 --- a/app/RSpade/Core/Mode/Rsx_Mode.php +++ /dev/null @@ -1,182 +0,0 @@ - 'Development', - self::DEBUG => 'Debug', - self::PRODUCTION => 'Production', - }; - } -} diff --git a/app/RSpade/Core/Rsx.php b/app/RSpade/Core/Rsx.php index f98d605a8..cad6cb4e0 100644 --- a/app/RSpade/Core/Rsx.php +++ b/app/RSpade/Core/Rsx.php @@ -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 */ diff --git a/app/RSpade/man/app_mode.txt b/app/RSpade/man/app_mode.txt index c89104dbf..854f45bf7 100755 --- a/app/RSpade/man/app_mode.txt +++ b/app/RSpade/man/app_mode.txt @@ -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. diff --git a/app/RSpade/man/rsxapp.txt b/app/RSpade/man/rsxapp.txt index 896569815..0d64d6e30 100755 --- a/app/RSpade/man/rsxapp.txt +++ b/app/RSpade/man/rsxapp.txt @@ -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: diff --git a/config/rsx.php b/config/rsx.php index 091702660..36b6a89be 100755 --- a/config/rsx.php +++ b/config/rsx.php @@ -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), ], /*