Files
rspade_system/app/RSpade/Core/Js/Ajax.js
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

199 lines
8.8 KiB
JavaScript
Executable File

// @FILE-SUBCLASS-01-EXCEPTION
/**
* Client-side Ajax class for making API calls to RSX controllers
*
* Mirrors the PHP Ajax::call (Ajax::internal) functionality for browser-side JavaScript
*/
class Ajax {
/**
* Make an AJAX call to an RSX controller action
*
* All calls are automatically batched using Rsx_Ajax_Batch unless
* window.rsxapp.ajax_disable_batching is true (for debugging).
*
* @param {string} controller - The controller class name (e.g., 'User_Controller')
* @param {string} action - The action method name (e.g., 'get_profile')
* @param {object} params - Parameters to send to the action
* @returns {Promise} - Resolves with the return value, rejects with error
*/
static async call(controller, action, params = {}) {
// Route through batch system
return Rsx_Ajax_Batch.call(controller, action, params);
}
/**
* DEPRECATED: Direct call implementation (preserved for reference)
* This is now handled by Rsx_Ajax_Batch
* @private
*/
static async _call_direct(controller, action, params = {}) {
// Build the endpoint URL
const url = `/_ajax/${controller}/${action}`;
// Log the AJAX call using console_debug
if (typeof Debugger !== 'undefined' && Debugger.console_debug) {
Debugger.console_debug('AJAX', `Calling ${controller}.${action}`, params);
}
return new Promise((resolve, reject) => {
$.ajax({
url: url,
method: 'POST',
data: params,
dataType: 'json',
__local_integration: true, // Bypass $.ajax override - this is the official Ajax endpoint pattern
success: (response) => {
// Handle console_debug messages if present
if (response.console_debug && Array.isArray(response.console_debug)) {
response.console_debug.forEach((msg) => {
// Messages must be structured as [channel, [arguments]]
if (!Array.isArray(msg) || msg.length !== 2) {
throw new Error('Invalid console_debug message format - expected [channel, [arguments]]');
}
const [channel, args] = msg;
// Output with channel as first argument, then spread the arguments
console.log(channel, ...args);
});
}
// Check if the response was successful
if (response.success === true) {
// Process the return value to instantiate any ORM models
const processedValue = Rsx_Js_Model._instantiate_models_recursive(response._ajax_return_value);
// Return the processed value
resolve(processedValue);
} else {
// Handle error responses
const error_type = response.error_type || 'unknown_error';
const reason = response.reason || 'Unknown error occurred';
const details = response.details || {};
// Handle specific error types
switch (error_type) {
case 'response_auth_required':
console.error(
'The user is no longer authenticated, this is a placeholder for future code which handles this scenario.'
);
// Create an error object similar to PHP exceptions
const auth_error = new Error(reason);
auth_error.type = 'auth_required';
auth_error.details = details;
reject(auth_error);
break;
case 'response_unauthorized':
console.error(
'The user is unauthorized to perform this action, this is a placeholder for future code which handles this scenario.'
);
const unauth_error = new Error(reason);
unauth_error.type = 'unauthorized';
unauth_error.details = details;
reject(unauth_error);
break;
case 'response_form_error':
// Form validation errors
const form_error = new Error(reason);
form_error.type = 'form_error';
form_error.details = details;
reject(form_error);
break;
case 'response_fatal_error':
// Fatal errors
const fatal_error = new Error(reason);
fatal_error.type = 'fatal_error';
fatal_error.details = details;
// Log to server if browser error logging is enabled
Debugger.log_error({
message: `Ajax Fatal Error: ${reason}`,
type: 'ajax_fatal',
endpoint: url,
details: details,
});
reject(fatal_error);
break;
default:
// Unknown error type
const generic_error = new Error(reason);
generic_error.type = error_type;
generic_error.details = details;
reject(generic_error);
break;
}
}
},
error: (xhr, status, error) => {
// Handle network or server errors
let error_message = 'Network or server error';
if (xhr.responseJSON && xhr.responseJSON.message) {
error_message = xhr.responseJSON.message;
} else if (xhr.responseText) {
try {
const response = JSON.parse(xhr.responseText);
if (response.message) {
error_message = response.message;
}
} catch (e) {
// If response is not JSON, use the status text
error_message = `${status}: ${error}`;
}
} else {
error_message = `${status}: ${error}`;
}
const network_error = new Error(error_message);
network_error.type = 'network_error';
network_error.status = xhr.status;
network_error.statusText = status;
// Log server errors (500+) to the server if browser error logging is enabled
if (xhr.status >= 500) {
Debugger.log_error({
message: `Ajax Server Error ${xhr.status}: ${error_message}`,
type: 'ajax_server_error',
endpoint: url,
status: xhr.status,
statusText: status,
});
}
reject(network_error);
},
});
});
}
/**
* Parses an AJAX URL into controller and action
* @param {string} url - URL in format '/_ajax/Controller_Name/action_name'
* @returns {Object} Object with {controller: string, action: string}
* @throws {Error} If URL doesn't start with /_ajax or has invalid structure
*/
static ajax_url_to_controller_action(url) {
if (!url.startsWith('/_ajax')) {
throw new Error(`URL must start with /_ajax, got: ${url}`);
}
const parts = url.split('/').filter((part) => part !== '');
if (parts.length < 2) {
throw new Error(`Invalid AJAX URL structure: ${url}`);
}
if (parts.length > 3) {
throw new Error(`AJAX URL has too many segments: ${url}`);
}
const controller = parts[1];
const action = parts[2] || 'index';
return { controller, action };
}
}