Update beads metadata

Fix Form_Utils bugs and unify error handling documentation
Protect framework files from auto-modification when not in developer mode

🤖 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 22:44:48 +00:00
parent 8d92b287be
commit 45cf44edeb
355 changed files with 82 additions and 71 deletions

View File

@@ -88,6 +88,12 @@ class Document_Models_Command extends FrameworkDeveloperCommand
continue;
}
// Skip framework files (app/RSpade/) unless in framework developer mode
// This prevents end users from accidentally modifying framework source
if (str_starts_with($filePath, 'app/RSpade/') && !config('rsx.code_quality.is_framework_developer', false)) {
continue;
}
$models[$className] = $fullPath;
}

View File

@@ -72,6 +72,12 @@ class Constants_Regenerate_Command extends Command
continue;
}
// Skip framework files (app/RSpade/) unless in framework developer mode
// This prevents end users from accidentally modifying framework source
if (str_starts_with($model_metadata['file'], 'app/RSpade/') && !config('rsx.code_quality.is_framework_developer', false)) {
continue;
}
// Silently process - only output changes/warnings
try {

View File

@@ -110,9 +110,9 @@ Error responses:
```json
{
"_success": false,
"error_type": "response_form_error",
"reason": "Validation failed",
"details": { /* error details */ }
"error_code": "validation",
"message": "Please correct the errors below",
"errors": { "field_name": "Error message" }
}
```
@@ -189,15 +189,15 @@ class User_Controller extends Rsx_Controller
public static function get_profile(Request $request, array $params = [])
{
$user_id = $params['user_id'] ?? null;
if (!$user_id) {
return response_form_error('User ID required', ['user_id' => 'Missing']);
return response_error(Ajax::ERROR_VALIDATION, ['user_id' => 'User ID required']);
}
$user = User::find($user_id);
if (!$user) {
return response_form_error('User not found', ['user_id' => 'Invalid']);
return response_error(Ajax::ERROR_NOT_FOUND, 'User not found');
}
return [
@@ -218,14 +218,19 @@ Normal responses (arrays, objects) are automatically filtered through `json_deco
This prevents memory leaks and ensures clean data structures.
## Special Response Types
## Error Response Helper
The following helper functions create special responses that are handled differently:
Use `response_error()` with error codes from the `Ajax` class:
- `response_auth_required($reason, $redirect)` - Throws `AjaxAuthRequiredException`
- `response_unauthorized($reason, $redirect)` - Throws `AjaxUnauthorizedException`
- `response_form_error($reason, $details)` - Throws `AjaxFormErrorException` with extractable details
- `response_fatal_error($reason, $details)` - Throws `AjaxFatalErrorException`
```php
return response_error(Ajax::ERROR_VALIDATION, ['field' => 'Error message']);
return response_error(Ajax::ERROR_NOT_FOUND, 'Resource not found');
return response_error(Ajax::ERROR_UNAUTHORIZED);
return response_error(Ajax::ERROR_AUTH_REQUIRED);
return response_error(Ajax::ERROR_FATAL, 'Something went wrong');
```
**Error codes:** `ERROR_VALIDATION`, `ERROR_NOT_FOUND`, `ERROR_UNAUTHORIZED`, `ERROR_AUTH_REQUIRED`, `ERROR_FATAL`, `ERROR_GENERIC`
## Security Considerations

View File

@@ -107,7 +107,7 @@ class Form_Utils {
// Resolve the promise once all animations are complete
Promise.all(animations).then(() => {
// Scroll to error container if it exists
const $error_container = $parent.find('[data-id="error_container"]').first();
const $error_container = $parent.find('[$sid="error_container"]').first();
if ($error_container.length > 0) {
const container_top = $error_container.offset().top;
@@ -177,8 +177,8 @@ class Form_Utils {
return response;
} catch (error) {
if (error.type === 'form_error' && error.details) {
await Form_Utils.apply_form_errors(form_selector, error.details);
if (error.code === Ajax.ERROR_VALIDATION && error.metadata) {
await Form_Utils.apply_form_errors(form_selector, error.metadata);
} else {
await Form_Utils.apply_form_errors(form_selector, error.message || 'An error occurred');
}
@@ -345,7 +345,7 @@ class Form_Utils {
*/
static _apply_combined_error($parent, summary_msg, unmatched_errors) {
const animations = [];
const $error_container = $parent.find('[data-id="error_container"]').first();
const $error_container = $parent.find('[$sid="error_container"]').first();
const $target = $error_container.length > 0 ? $error_container : $parent;
// Create alert with summary message and bulleted list of unmatched errors
@@ -386,7 +386,7 @@ class Form_Utils {
const animations = [];
// Look for a specific error container div (e.g., in Rsx_Form component)
const $error_container = $parent.find('[data-id="error_container"]').first();
const $error_container = $parent.find('[$sid="error_container"]').first();
const $target = $error_container.length > 0 ? $error_container : $parent;
if (typeof messages === 'string') {

View File

@@ -857,10 +857,10 @@ class Rsx {
<p class="mb-0">${Rsx._escape_html(message)}</p>
</div>
`;
} else if (error.type === 'form_error' && error.details) {
} else if (error.code === Ajax.ERROR_VALIDATION && error.metadata) {
// Validation errors - show unmatched errors only
// (matched errors should be handled by Form_Utils.apply_form_errors)
const errors = error.details;
const errors = error.metadata;
const error_list = [];
for (const field in errors) {

View File

@@ -26,9 +26,9 @@ RESPONSE FORMATS
-----------------------------------------
{
"_success": false,
"error_type": "response_form_error|response_auth_required|response_unauthorized",
"error_code": "validation|not_found|unauthorized|auth_required",
"reason": "User-friendly message",
"details": { /* optional, e.g., field-specific validation errors */ },
"metadata": { /* optional, e.g., field-specific validation errors */ },
"console_debug": [ /* optional debug messages */ ]
}
@@ -84,35 +84,42 @@ HTTP STATUS CODE STRATEGY
ERROR TYPES
fatal
-----
All error responses use the unified response_error() function with constants:
Ajax::ERROR_FATAL
-----------------
Uncaught PHP exceptions, database errors, bugs in code.
Dev mode: Shows file/line/error/backtrace
Prod mode: Generic "An error occurred" message
response_form_error (validation)
--------------------------------
Ajax::ERROR_VALIDATION
----------------------
Validation failures with field-specific details.
Used via: return response_form_error($reason, $details);
Used via: return response_error(Ajax::ERROR_VALIDATION, $field_errors);
Example:
return response_form_error('Validation failed', [
return response_error(Ajax::ERROR_VALIDATION, [
'email' => 'Email address is required',
'phone' => 'Invalid phone number format'
]);
response_auth_required
----------------------
Ajax::ERROR_AUTH_REQUIRED
-------------------------
User not logged in, needs authentication.
Used via: return response_auth_required($reason, $redirect);
Used via: return response_error(Ajax::ERROR_AUTH_REQUIRED);
response_unauthorized
---------------------
Ajax::ERROR_UNAUTHORIZED
------------------------
User logged in but lacks permission for this action.
Used via: return response_unauthorized($reason, $redirect);
Used via: return response_error(Ajax::ERROR_UNAUTHORIZED);
network
-------
Client-side only. Server unreachable (xhr.status === 0).
Ajax::ERROR_NOT_FOUND
---------------------
Resource not found.
Used via: return response_error(Ajax::ERROR_NOT_FOUND, 'Project not found');
network (client-side only)
--------------------------
Server unreachable (xhr.status === 0).
SERVER-SIDE IMPLEMENTATION
@@ -127,7 +134,7 @@ SERVER-SIDE IMPLEMENTATION
$user = User::find($params['id']);
if (!$user) {
return response_form_error('User not found', ['id' => 'Invalid']);
return response_error(Ajax::ERROR_NOT_FOUND, 'User not found');
}
return [
@@ -216,13 +223,17 @@ CLIENT-SIDE IMPLEMENTATION
----------------------
Rejected promises receive standard Error objects with additional properties:
error.type - 'fatal'|'form_error'|'auth_required'|
'unauthorized'|'network'
error.code - 'validation'|'not_found'|'unauthorized'|
'auth_required'|'fatal'|'network'
error.message - User-displayable message
error.details - Full error data from server
(for form_error: field-specific errors)
error.metadata - Additional error data from server
(for validation: field-specific errors)
(for fatal: file/line/backtrace)
Constants available on both server and client:
PHP: Ajax::ERROR_VALIDATION, Ajax::ERROR_NOT_FOUND, etc.
JS: Ajax.ERROR_VALIDATION, Ajax.ERROR_NOT_FOUND, etc.
Automatic Error Handling
------------------------
Uncaught Ajax errors automatically display via Modal.error():
@@ -253,7 +264,7 @@ CLIENT-SIDE IMPLEMENTATION
});
Rsx_Form.render_error() handles all error types:
- form_error: Shows inline field errors + unmatched errors alert
- validation: Shows inline field errors + unmatched errors alert
- fatal/network/auth: Shows error in form's error container
Form_Utils Error Display
@@ -264,8 +275,8 @@ CLIENT-SIDE IMPLEMENTATION
const result = await Controller.save_user(form_data);
await Modal.alert('User saved!');
} catch (error) {
if (error.type === 'form_error') {
await Form_Utils.apply_form_errors($form, error.details);
if (error.code === Ajax.ERROR_VALIDATION) {
await Form_Utils.apply_form_errors($form, error.metadata);
} else {
Rsx.render_error(error, '#error_container');
}
@@ -289,7 +300,7 @@ CLIENT-SIDE IMPLEMENTATION
Handles all error types:
- fatal: Shows file:line and full message
- form_error: Shows bulleted list of unmatched validation errors
- validation: Shows bulleted list of unmatched validation errors
- auth/network: Shows user-friendly message
- generic: Shows error message in danger alert
@@ -403,7 +414,7 @@ COMMON PATTERNS
public static function save(Request $request, array $params = []): array
{
if (empty($params['email'])) {
return response_form_error('Validation failed', [
return response_error(Ajax::ERROR_VALIDATION, [
'email' => 'Email is required'
]);
}