diff --git a/src/lib/ae_api/api_get_object.ts b/src/lib/ae_api/api_get_object.ts index 46ead7db..fbf0b463 100644 --- a/src/lib/ae_api/api_get_object.ts +++ b/src/lib/ae_api/api_get_object.ts @@ -184,7 +184,21 @@ export const get_object = async function get_object({ return false; } - console.log('The response was not ok. Throwing an error!'); + // Structured Error Handling (V3): Attempt to get rich error metadata + let error_json: any = null; + try { + error_json = await response.json(); + } catch (e) { + // Not JSON + } + + if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json); + + if (error_json?.meta?.details) { + // Return the rich error object so caller can handle specific categories (database_schema, etc) + return error_json; + } + throw new Error(`HTTP error! status: ${response.status}`); } diff --git a/src/lib/ae_api/api_post_object.ts b/src/lib/ae_api/api_post_object.ts index 1d7fb78f..5e7f0ca6 100644 --- a/src/lib/ae_api/api_post_object.ts +++ b/src/lib/ae_api/api_post_object.ts @@ -164,19 +164,34 @@ export const post_object = async function post_object({ if (!response.ok) { if (response.status === 404) { - console.warn('404 Not Found. Returning null.'); + if (log_lvl) { + console.log('The response was a 404 not found "error". Returning null.'); + } return null; } - - const errorBody = await response.text(); - console.error(`HTTP error! status: ${response.status}`, errorBody); - // Don't retry on client errors (400, 401, 403) - if (response.status >= 400 && response.status < 404) { + // FAIL FAST (Section 2D): Do not retry on Auth/Permission failures + if (response.status === 401 || response.status === 403) { + console.error(`API Auth Failure (${response.status}). Failing fast as per Section 2D protocol.`); return false; } - throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`); + // Structured Error Handling (V3): Attempt to get rich error metadata + let error_json: any = null; + try { + error_json = await response.json(); + } catch (e) { + // Not JSON + } + + if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json); + + if (error_json?.meta?.details) { + // Return the rich error object so caller can handle specific categories (database_schema, etc) + return error_json; + } + + throw new Error(`HTTP error! status: ${response.status}`); } if (!return_blob) { diff --git a/src/routes/testing/+page.svelte b/src/routes/testing/+page.svelte index 7771bf2d..0560af47 100644 --- a/src/routes/testing/+page.svelte +++ b/src/routes/testing/+page.svelte @@ -176,6 +176,53 @@ return await response.json(); }); + // V3 Schema & Error Validation + const test_permissive_mode = () => run_test('Permissive Mode Test', async () => { + const endpoint = `/v3/crud/account/${$ae_loc.account_id || 'ghost'}`; + const data = { + name: $ae_loc.account_name, + _non_existent_field: 'This should be ignored by the API' + }; + // Use post_object for a patch-like operation if needed, or update_ae_obj_v3 style + // For testing purposes, we'll use a standard fetch to see raw response + const url = new URL(endpoint, $ae_api.base_url); + const headers = { + ...$ae_api.headers, + 'x-account-id': $ae_loc.account_id || 'ghost', + 'Authorization': `Bearer ${$ae_loc.jwt}` + }; + + const response = await fetch(url.toString(), { + method: 'PATCH', + headers, + body: JSON.stringify(data) + }); + + const result = await response.json(); + return { status: response.status, result }; + }); + + const test_structured_error = () => run_test('Structured Error Validation (Deliberate 400)', async () => { + const endpoint = `/v3/crud/account/${$ae_loc.account_id || 'ghost'}`; + // To trigger a 400 error despite permissive mode, we'll try to set a read-only field or invalid type if possible + // Actually, the easiest way to test structured error is to send a malformed filter to /search + const url = new URL(`${endpoint.split('/account')[0]}/account/search`, $ae_api.base_url); + const headers = { + ...$ae_api.headers, + 'x-account-id': $ae_loc.account_id || 'ghost', + 'Authorization': `Bearer ${$ae_loc.jwt}` + }; + + const response = await fetch(url.toString(), { + method: 'POST', + headers, + body: JSON.stringify({ and: [{ field: "non_existent", op: "eq", value: "fail" }] }) + }); + + const result = await response.json(); + return { status: response.status, structured_details: result.meta?.details || 'MISSING' }; + }); + // Environment Diagnostics let is_native = $derived(typeof window !== 'undefined' && !!(window as any).native_app); let app_mode = $derived($events_loc?.launcher?.app_mode || 'web'); @@ -443,6 +490,18 @@ Load Accounts +
+
+ +

V3 Hardening

+
+ + +