diff --git a/app/RSpade/Core/Bundle/BundleCompiler.php b/app/RSpade/Core/Bundle/BundleCompiler.php index 097b6f0fe..754507317 100755 --- a/app/RSpade/Core/Bundle/BundleCompiler.php +++ b/app/RSpade/Core/Bundle/BundleCompiler.php @@ -1831,13 +1831,6 @@ implode("\n", array_map(fn ($f) => ' - ' . str_replace(base_path() . '/', '', $files['js'][] = $manifest_file; } - // Generate JQHTML cache class registration (must come after manifest) - // Registers all ES6 classes with jqhtml for proper data cache serialization - $jqhtml_registration_file = $this->_create_jqhtml_class_registration($files['js'] ?? []); - if ($jqhtml_registration_file) { - $files['js'][] = $jqhtml_registration_file; - } - // Generate route definitions for JavaScript $route_file = $this->_create_javascript_routes(); if ($route_file) { @@ -2462,92 +2455,6 @@ implode("\n", array_map(fn ($f) => ' - ' . str_replace(base_path() . '/', '', return $this->_write_temp_file($js_code, 'js'); } - /** - * Create JQHTML cache class registration code - * - * Registers all ES6 classes with jqhtml for proper serialization during data caching. - * In data cache mode, jqhtml enforces hot/cold cache parity - any class instance stored - * in this.data that is NOT registered will be converted to a plain object (properties - * preserved but prototype methods lost). - * - * This code runs immediately after Manifest._define() so classes are available. - * - * @param array $js_files Array of JavaScript file paths in the bundle - * @return string|null Path to temp file containing registration code, or null if no classes - */ - protected function _create_jqhtml_class_registration(array $js_files): ?string - { - $manifest_files = Manifest::get_all(); - $class_names = []; - - // Collect all class names from JS files (same logic as _create_javascript_manifest) - foreach ($js_files as $file) { - // Skip temp files except auto-generated model classes - if (str_contains($file, 'storage/rsx-tmp/')) { - if (str_contains($file, 'bundle_generated_models_')) { - $content = file_get_contents($file); - if (preg_match_all('/class\s+([A-Za-z_][A-Za-z0-9_]*)\s+extends/', $content, $matches)) { - foreach ($matches[1] as $class_name) { - $class_names[] = $class_name; - } - } - } - continue; - } - - // Handle stub files - if (str_contains($file, 'storage/rsx-build/js-stubs/') || str_contains($file, 'storage/rsx-build/js-model-stubs/')) { - $stub_content = file_get_contents($file); - $stub_metadata = $this->_extract_stub_class_info($stub_content); - if (!empty($stub_metadata['class'])) { - $class_names[] = $stub_metadata['class']; - } - continue; - } - - // Get class name from manifest - $relative = str_replace(base_path() . '/', '', $file); - $file_data = $manifest_files[$relative] ?? null; - - if ($file_data && !empty($file_data['class'])) { - $class_name = $file_data['class']; - // Skip 'object' as it's not a real class - if (strtolower($class_name) !== 'object') { - $class_names[] = $class_name; - } - } - } - - // If no classes found, return null - if (empty($class_names)) { - return null; - } - - // Remove duplicates and sort for deterministic output - $class_names = array_unique($class_names); - sort($class_names); - - // Generate JavaScript code for jqhtml class registration - $js_code = "// JQHTML Cache Class Registration - Generated by BundleCompiler\n"; - $js_code .= "// Registers all ES6 classes for proper serialization in jqhtml data caching.\n"; - $js_code .= "// Without registration, class instances in this.data become plain objects on cache restore.\n"; - $js_code .= "(function() {\n"; - $js_code .= " if (typeof jqhtml === 'undefined' || typeof jqhtml.register_cache_class !== 'function') {\n"; - $js_code .= " return; // jqhtml not available or doesn't support cache class registration\n"; - $js_code .= " }\n"; - - foreach ($class_names as $class_name) { - // Check that class exists before registering (safety check) - $js_code .= " if (typeof {$class_name} !== 'undefined') jqhtml.register_cache_class({$class_name});\n"; - } - - $js_code .= "})();\n"; - - // Write to temporary file - return $this->_write_temp_file($js_code, 'js'); - } - - /** * Create JavaScript runner for automatic class initialization */ diff --git a/app/RSpade/Integrations/Jqhtml/Jqhtml_Integration.js b/app/RSpade/Integrations/Jqhtml/Jqhtml_Integration.js index eefdf6bd8..4ca13f476 100755 --- a/app/RSpade/Integrations/Jqhtml/Jqhtml_Integration.js +++ b/app/RSpade/Integrations/Jqhtml/Jqhtml_Integration.js @@ -98,12 +98,26 @@ class Jqhtml_Integration { // Data Cache Mode (jqhtml 2.x): // - Enforces hot/cold cache parity - fresh data and cached data behave identically // - Any ES6 class instance stored in this.data must be registered with jqhtml - // - Class registration is done by BundleCompiler via register_cache_class() // - Without registration, class instances become plain objects on cache restore // (properties preserved but prototype methods lost) // ───────────────────────────────────────────────────────────────────── jqhtml.set_cache_key(Rsx.scope_key(), 'data'); window.jqhtml.debug.verbose = false; + + // ───────────────────────────────────────────────────────────────────── + // Register All Classes with jqhtml for Cache Serialization + // + // Loop through all classes already registered with Manifest and register + // them with jqhtml. This enables proper serialization of class instances + // stored in this.data during data caching. + // ───────────────────────────────────────────────────────────────────── + if (typeof jqhtml.register_cache_class === 'function') { + const all_classes = Manifest.get_all_classes(); + for (const class_info of all_classes) { + jqhtml.register_cache_class(class_info.class_object); + } + console_debug('JQHTML_INIT', `Registered ${all_classes.length} classes for cache serialization`); + } } /**