Add SPA session validation and buglist, update migration docs

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-03 21:28:08 +00:00
parent 9be3dfc14e
commit cff287e870
24169 changed files with 10223 additions and 7120 deletions

View File

@@ -102,14 +102,19 @@ class JqhtmlWebpackCompiler
// Compile via RPC server
$compiled_js = static::_compile_via_rpc($file_path);
// Don't add any comments - they break sourcemap line offsets
// Just use the compiler output as-is
// Extract template variable name and append registration call
// CRITICAL: Do NOT add extra newlines - they break sourcemap line offsets
// The registration is appended AFTER the sourcemap comment so it doesn't affect mappings
if (preg_match('/var\s+(template_\w+)\s*=/', $compiled_js, $matches)) {
$template_var = $matches[1];
// Append registration on same line as end of sourcemap (no extra newlines)
$compiled_js = rtrim($compiled_js) . "\njqhtml.register_template({$template_var});";
}
$wrapped_js = $compiled_js;
// Ensure proper newline at end
if (!str_ends_with($wrapped_js, "\n")) {
$wrapped_js .= "\n";
}
// Ensure exactly one newline at end (no extra)
$wrapped_js = rtrim($wrapped_js) . "\n";
// Cache the compiled result
file_put_contents($cache_file, $wrapped_js);

View File

@@ -49,11 +49,22 @@ class Jqhtml_Integration {
// ─────────────────────────────────────────────────────────────────────
// Tag Static Methods with Cache IDs
//
// jqhtml caches component output based on args. When a function is passed
// as an arg (e.g., DataGrid's data_source), we need a stable string key.
// jqhtml caches component renders based on a hash of their args.
// Problem: Functions can't be serialized, so passing one (e.g., a
// DataGrid's data_source callback) would defeat caching entirely.
//
// Without this: data_source: function() {...} → no cache (functions aren't serializable)
// With this: function._jqhtml_cache_id = "My_DataGrid.fetch_data" → cacheable
// Solution: Tag every static method with a stable string identifier.
// When jqhtml hashes component args, it uses _jqhtml_cache_id instead
// of the function reference, making the cache key deterministic.
//
// Example:
// <My_DataGrid $data_source=Controller.fetch />
//
// Without tagging: args hash includes [Function] → uncacheable
// With tagging: args hash includes "Controller.fetch" → cacheable
//
// This enables Ajax endpoints and other callbacks to be passed to
// components without breaking the automatic caching system.
// ─────────────────────────────────────────────────────────────────────
const all_classes = Manifest.get_all_classes();
let methods_tagged = 0;
@@ -106,20 +117,6 @@ class Jqhtml_Integration {
Rsx.trigger('jqhtml_ready');
});
}
// ═══════════════════════════════════════════════════════════════════════
// Utility Methods (pass-through to jqhtml runtime)
// ═══════════════════════════════════════════════════════════════════════
/** Get all registered component names. Useful for debugging/introspection. */
static get_component_names() {
return jqhtml.get_component_names();
}
/** Check if a component is registered by name. */
static has_component(name) {
return jqhtml.has_component(name);
}
}
// Class is automatically made global by RSX manifest - no window assignment needed

View File

@@ -1,3 +1,3 @@
.Component_Init {
display:none;
._Component_Init {
display: none;
}

View File

@@ -1,236 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JQHTML Compilation Error</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
margin: 0;
padding: 0;
background: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 40px auto;
padding: 0 20px;
}
.error-header {
background: #fff;
border: 1px solid #e1e1e1;
border-radius: 8px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.error-title {
color: #e74c3c;
font-size: 24px;
margin: 0 0 10px 0;
font-weight: 600;
}
.error-file {
color: #666;
font-size: 14px;
margin: 10px 0;
font-family: 'Courier New', monospace;
}
.error-location {
color: #999;
font-size: 13px;
}
.error-message {
background: #fff;
border: 1px solid #e1e1e1;
border-radius: 8px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.error-message h3 {
color: #555;
font-size: 16px;
margin: 0 0 15px 0;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
}
.error-text {
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
word-wrap: break-word;
margin: 0;
padding: 20px;
background: #f8f8f8;
border-radius: 4px;
border-left: 4px solid #e74c3c;
color: #444;
}
.error-context {
background: #fff;
border: 1px solid #e1e1e1;
border-radius: 8px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.error-context h3 {
color: #555;
font-size: 16px;
margin: 0 0 15px 0;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
}
.code-context {
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.5;
background: #2d2d2d;
color: #f8f8f2;
padding: 20px;
border-radius: 4px;
overflow-x: auto;
white-space: pre;
}
.error-line {
background: rgba(231, 76, 60, 0.2);
display: inline-block;
width: 100%;
padding: 0 5px;
margin: 0 -5px;
}
.line-number {
color: #999;
display: inline-block;
width: 40px;
text-align: right;
padding-right: 10px;
border-right: 1px solid #444;
margin-right: 10px;
}
.error-suggestion {
background: #fff;
border: 1px solid #e1e1e1;
border-radius: 8px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.error-suggestion h3 {
color: #27ae60;
font-size: 16px;
margin: 0 0 15px 0;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
}
.suggestion-text {
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
padding: 20px;
background: #f0f9f4;
border-radius: 4px;
border-left: 4px solid #27ae60;
color: #2c5f2d;
}
.error-trace {
background: #fff;
border: 1px solid #e1e1e1;
border-radius: 8px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.error-trace h3 {
color: #555;
font-size: 16px;
margin: 0 0 15px 0;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
}
.trace-list {
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.8;
}
.trace-item {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
}
.trace-item:last-child {
border-bottom: none;
}
.trace-number {
color: #999;
display: inline-block;
width: 30px;
}
.trace-file {
color: #666;
}
.trace-function {
color: #3498db;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="error-header">
<h1 class="error-title">JQHTML Template Compilation Error</h1>
<div class="error-file">{{ $exception->getFile() }}</div>
@if($exception->getLine())
<div class="error-location">Line {{ $exception->getLine() }}@if(method_exists($exception, 'getColumn') && $exception->getColumn()), Column {{ $exception->getColumn() }}@endif</div>
@endif
</div>
<div class="error-message">
<h3>Error Message</h3>
<pre class="error-text">{{ $exception->getMessage() }}</pre>
</div>
@if(method_exists($exception, 'getContext') && $exception->getContext())
<div class="error-context">
<h3>Code Context</h3>
<pre class="code-context">{{ $exception->getContext() }}</pre>
</div>
@endif
@if(method_exists($exception, 'getSuggestion') && $exception->getSuggestion())
<div class="error-suggestion">
<h3>How to Fix</h3>
<pre class="suggestion-text">{{ $exception->getSuggestion() }}</pre>
</div>
@endif
@if(app()->environment('local', 'development'))
<div class="error-trace">
<h3>Stack Trace</h3>
<div class="trace-list">
@foreach($exception->getTrace() as $index => $trace)
@if($index < 10)
<div class="trace-item">
<span class="trace-number">#{{ $index }}</span>
<span class="trace-file">{{ $trace['file'] ?? 'unknown' }}:{{ $trace['line'] ?? '?' }}</span>
@if(isset($trace['class']))
<span class="trace-function">{{ $trace['class'] }}{{ $trace['type'] ?? '::' }}{{ $trace['function'] }}()</span>
@elseif(isset($trace['function']))
<span class="trace-function">{{ $trace['function'] }}()</span>
@endif
</div>
@endif
@endforeach
</div>
</div>
@endif
</div>
</body>
</html>