Add SPA enable/disable functionality and graceful error handling

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-20 20:51:17 +00:00
parent c60cd84e57
commit 081fc0b88e
4 changed files with 184 additions and 49 deletions

View File

@@ -29,25 +29,9 @@ class Debugger {
static _on_framework_core_init() {
// Check if browser error logging is enabled
if (window.rsxapp && window.rsxapp.log_browser_errors) {
// Register global error handler
window.addEventListener('error', function (event) {
Debugger._handle_browser_error({
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error ? event.error.stack : null,
type: 'error',
});
});
// Register unhandled promise rejection handler
window.addEventListener('unhandledrejection', function (event) {
Debugger._handle_browser_error({
message: event.reason ? event.reason.message || String(event.reason) : 'Unhandled promise rejection',
stack: event.reason && event.reason.stack ? event.reason.stack : null,
type: 'unhandledrejection',
});
// Listen for unhandled exceptions from Rsx event system
Rsx.on('unhandled_exception', function (error_data) {
Debugger._handle_browser_error(error_data);
});
}

View File

@@ -50,6 +50,9 @@ class Rsx {
// Gets set to true to interupt startup sequence
static __stopped = false;
// Timestamp of last flash alert for unhandled exceptions (rate limiting)
static _last_exception_flash = 0;
// Initialize event handlers storage
static _init_events() {
if (typeof Rsx._event_handlers === 'undefined') {
@@ -107,6 +110,75 @@ class Rsx {
this.trigger('refresh');
}
/**
* Setup global unhandled exception handlers
* Must be called before framework initialization begins
*/
static _setup_exception_handlers() {
// Handle uncaught JavaScript errors
window.addEventListener('error', function (event) {
Rsx._handle_unhandled_exception({
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error ? event.error.stack : null,
type: 'error',
error: event.error,
});
});
// Handle unhandled promise rejections
window.addEventListener('unhandledrejection', function (event) {
Rsx._handle_unhandled_exception({
message: event.reason ? event.reason.message || String(event.reason) : 'Unhandled promise rejection',
stack: event.reason && event.reason.stack ? event.reason.stack : null,
type: 'unhandledrejection',
error: event.reason,
});
});
}
/**
* Internal handler for unhandled exceptions
* Triggers event, shows flash alert (rate limited), disables SPA, and logs to console
*/
static _handle_unhandled_exception(error_data) {
// Always log to console
console.error('[Rsx] Unhandled exception:', error_data);
// Trigger event for listeners (e.g., Debugger for server logging)
Rsx.trigger('unhandled_exception', error_data);
// Disable SPA navigation if in SPA mode
// This allows user to navigate away from broken page using normal browser navigation
if (typeof Spa !== 'undefined' && window.rsxapp && window.rsxapp.is_spa) {
Spa.disable();
}
// Show flash alert (rate limited to 1 per second)
const now = Date.now();
if (now - Rsx._last_exception_flash >= 1000) {
Rsx._last_exception_flash = now;
// Determine message based on dev/prod mode
let message;
if (window.rsxapp && window.rsxapp.debug) {
// Dev mode: Show actual error (shortened to 300 chars)
const error_text = error_data.message || 'Unknown error';
message = error_text.length > 300 ? error_text.substring(0, 300) + '...' : error_text;
} else {
// Production mode: Generic message
message = 'An unhandled error has occurred, you may need to refresh your page';
}
// Show flash alert if Flash_Alert is available
if (typeof Flash_Alert !== 'undefined') {
Flash_Alert.error(message);
}
}
}
// Log to server that an event happened
static log(type, message = 'notice') {
Core_Log.log(type, message);
@@ -502,6 +574,9 @@ class Rsx {
Rsx.__booted = true;
// Setup exception handlers first, before any initialization phases
Rsx._setup_exception_handlers();
// Get all registered classes from the manifest
const all_classes = Manifest.get_all_classes();