Implemented offline-first fast-paths and hardened API/Layout resilience. Added reactive offline banner, root error page, and ghost site fallbacks to handle server downtime and connection loss without crashing.

This commit is contained in:
Scott Idem
2026-01-16 16:41:32 -05:00
parent 8b611e7875
commit a10accfaaf
14 changed files with 536 additions and 564 deletions

View File

@@ -51,6 +51,12 @@ export const get_object = async function get_object({
return false;
}
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) {
if (log_lvl) console.log('get_object: Browser is offline. Failing fast to allow cache fallback.');
return false;
}
const url = new URL(endpoint, api_cfg['base_url']);
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
@@ -110,6 +116,12 @@ export const get_object = async function get_object({
}
for (let attempt = 1; attempt <= retry_count; attempt++) {
// FAIL FAST: Check if we are explicitly offline to avoid long browser timeouts
if (typeof navigator !== 'undefined' && !navigator.onLine) {
if (log_lvl) console.log(`get_object: Browser is offline (attempt ${attempt}). Failing fast to allow cache fallback.`);
return false;
}
try {
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
@@ -118,9 +130,17 @@ export const get_object = async function get_object({
'API GET Object *fetch* request was aborted or failed in an unexpected way.',
error
);
// Return the error so we can check it
return error;
});
clearTimeout(timeoutId);
// If the catch returned an Error object instead of a Response
if (response instanceof Error || (response && response.name === 'TypeError')) {
console.log('API GET Object: Detected NetworkError or TypeError. Failing fast.');
return false; // Skip retries for dead servers/DNS
}
if (!response) {
if (log_lvl > 1) {
console.log(