perf(hydration): optimize page loads and silence background fetch noise

- Implemented SWR pattern for Journal and Site Domain lookups.
- Refactored +layout.ts across modules to fire background refreshes instead of blocking.
- Updated +layout.svelte to render the layout shell immediately while hydrating.
- Silenced 'AbortError' and 'NetworkError' in api_get_object.ts and api_post_object.ts for log_lvl 0.
- Resolved duplicate export errors in ae_journals__journal.ts.
This commit is contained in:
Scott Idem
2026-01-26 17:06:22 -05:00
parent 715cc7cb65
commit c7a517a6e1
5 changed files with 150 additions and 392 deletions

View File

@@ -158,19 +158,29 @@ export const get_object = async function get_object({
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
// SILENCE NOISE: Aborted requests (common in SWR/Background loads) shouldn't spam logs
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
if (log_lvl > 1) {
console.log('API GET: Request was aborted or terminated by browser. This is expected during navigation.', error);
}
return error; // Return error to be handled below
}
console.log(
'API GET Object *fetch* request was aborted or failed in an unexpected way.',
'API GET Object *fetch* request 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
// Check if we should stop due to abort or network failure
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
// If it was an explicit abort, definitely stop
if (response.name === 'AbortError') return false;
if (log_lvl > 1) console.log('API GET Object: Detected NetworkError or TypeError. Failing fast.');
return false;
}
if (!response) {

View File

@@ -5,7 +5,7 @@ export const post_blob_percent_completed = temp_post_blob_percent_completed;
export const temp_post_object_percent_completed = 0;
export const post_object_percent_completed = temp_post_object_percent_completed;
// Updated 2026-01-08
// Updated 2026-01-26 (Abort Silence)
export const post_object = async function post_object({
api_cfg = null,
endpoint = '',
@@ -169,13 +169,29 @@ export const post_object = async function post_object({
const response = await fetch_method(url.toString(), fetchOptions).catch(function (
error: any
) {
// SILENCE NOISE: Aborted requests shouldn't spam logs at log_lvl 0
if (error.name === 'AbortError' || error.message?.includes('aborted') || error.name === 'TypeError') {
if (log_lvl > 1) {
console.log('API POST: Request was aborted or terminated by browser. Expected during navigation.', error);
}
return error;
}
console.log(
'API POST Object *fetch* request was aborted or failed in an unexpected way.',
'API POST Object *fetch* request failed in an unexpected way.',
error
);
return error;
});
clearTimeout(timeoutId);
// Check if we should stop due to abort or network failure
if (response instanceof Error || (response && (response.name === 'TypeError' || response.name === 'AbortError'))) {
if (response.name === 'AbortError') return false;
if (log_lvl > 1) console.log('API POST Object: Detected NetworkError or TypeError. Failing fast.');
return false;
}
if (!response) {
throw new Error(
`HTTP fetch request was aborted or failed in an unexpected way! URL = ${url.toString()}`

View File

@@ -11,7 +11,7 @@ import { load_ae_obj_li__journal_entry } from '$lib/ae_journals/ae_journals__jou
const ae_promises: key_val = {};
// Updated 2026-01-05
// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_id__journal({
api_cfg,
journal_id,
@@ -46,75 +46,62 @@ export async function load_ae_obj_id__journal({
log_lvl?: number;
}): Promise<ae_Journal | null> {
if (log_lvl) {
console.log(`*** load_ae_obj_id__journal() *** journal_id=${journal_id}`);
console.log(`*** load_ae_obj_id__journal() *** journal_id=${journal_id} (SWR)`);
}
ae_promises.load__journal_obj = await api
.get_ae_obj_v3({
api_cfg: api_cfg,
obj_type: 'journal',
obj_id: journal_id,
view,
params,
log_lvl: log_lvl
})
.then(async function (journal_obj_get_result) {
if (journal_obj_get_result) {
if (try_cache) {
// Process the results first
const processed_obj_li = await process_ae_obj__journal_props({
obj_li: [journal_obj_get_result],
log_lvl: log_lvl
});
if (log_lvl) {
console.log('Processed object list:', processed_obj_li);
}
// Save the updated results list to the database
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
// 1. FAST PATH: Return cached data immediately
if (try_cache) {
try {
const cached = await db_journals.journal.get(journal_id);
if (cached) {
if (log_lvl) console.log('JOURNAL LOAD: Cache hit. Returning stale data.');
_refresh_journal_id_background({
api_cfg, journal_id, view, params, try_cache,
inc_entry_li, enabled, hidden, limit, offset, order_by_li, log_lvl: 0
});
if (inc_entry_li && !cached.journal_entry_li) {
cached.journal_entry_li = await load_ae_obj_li__journal_entry({
api_cfg, for_obj_type: 'journal', for_obj_id: journal_id,
enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl
});
}
return journal_obj_get_result;
} else {
console.log('No results returned.');
return null;
return cached;
}
})
.catch(function (error: any) {
console.log('No results returned or failed.', error);
});
if (!ae_promises.load__journal_obj) {
console.log(`ERROR: Journals - Journal - The journal with ID ${journal_id} was not found.`);
return null;
} catch (e) {}
}
if (inc_entry_li) {
// Load the entries for the journal
const load_journal_entry_obj_li = await load_ae_obj_li__journal_entry({
api_cfg: api_cfg,
for_obj_type: 'journal',
for_obj_id: journal_id,
enabled: enabled,
hidden: hidden,
limit: limit,
offset: offset,
order_by_li: order_by_li,
params: params,
try_cache: try_cache,
log_lvl: log_lvl
});
ae_promises.load__journal_obj.journal_entry_li = load_journal_entry_obj_li;
}
return ae_promises.load__journal_obj;
// 2. SLOW PATH: Wait for API
return await _refresh_journal_id_background({
api_cfg, journal_id, view, params, try_cache,
inc_entry_li, enabled, hidden, limit, offset, order_by_li, log_lvl
});
}
// Updated 2026-01-02
/**
* Internal background refresh for a single journal
*/
async function _refresh_journal_id_background({ api_cfg, journal_id, view, params, try_cache, inc_entry_li, enabled, hidden, limit, offset, order_by_li, log_lvl }: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
try {
const result = await api.get_ae_obj_v3({ api_cfg, obj_type: 'journal', obj_id: journal_id, view, params, log_lvl });
if (result) {
if (try_cache) {
const processed = await process_ae_obj__journal_props({ obj_li: [result], log_lvl });
await db_save_ae_obj_li__ae_obj({ db_instance: db_journals, table_name: 'journal', obj_li: processed, properties_to_save, log_lvl });
}
if (inc_entry_li) {
result.journal_entry_li = await load_ae_obj_li__journal_entry({
api_cfg, for_obj_type: 'journal', for_obj_id: journal_id,
enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl
});
}
return result;
}
} catch (e) {}
return null;
}
// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_li__journal({
api_cfg,
for_obj_type = 'account',
@@ -151,139 +138,65 @@ export async function load_ae_obj_li__journal({
log_lvl?: number;
}): Promise<ae_Journal[]> {
if (log_lvl) {
console.log(
`*** load_ae_obj_li__journal() *** for_obj_type=${for_obj_type} for_obj_id=${for_obj_id}`
);
console.log(`*** load_ae_obj_li__journal() *** for=${for_obj_type}:${for_obj_id} (SWR)`);
}
let promise;
if (qry_person_id) {
const search_query: any = {
and: [{ field: 'person_id_random', op: 'eq', value: qry_person_id }]
};
if (for_obj_id) {
search_query.and.push({
field: `${for_obj_type}_id_random`,
op: 'eq',
value: for_obj_id
});
}
// Add enabled/hidden filters to search query as V3 search is explicit
if (enabled === 'enabled') {
search_query.and.push({ field: 'enable', op: 'eq', value: true });
} else if (enabled === 'not_enabled') {
search_query.and.push({ field: 'enable', op: 'eq', value: false });
}
if (hidden === 'hidden') {
search_query.and.push({ field: 'hide', op: 'eq', value: true });
} else if (hidden === 'not_hidden') {
search_query.and.push({ field: 'hide', op: 'eq', value: false });
}
if (log_lvl) {
console.log('Using search_ae_obj_v3 with query:', search_query);
}
promise = api.search_ae_obj_v3({
api_cfg,
obj_type: 'journal',
search_query,
order_by_li,
limit,
offset,
log_lvl
});
} else {
promise = api.get_ae_obj_li_v3({
api_cfg,
obj_type: 'journal',
for_obj_type,
for_obj_id,
enabled,
hidden,
limit,
offset,
order_by_li,
log_lvl
});
}
ae_promises.load__journal_obj_li = await promise.then(async function (journal_obj_li_get_result) {
if (journal_obj_li_get_result) {
if (try_cache) {
// Process the results first
const processed_obj_li = await process_ae_obj__journal_props({
obj_li: journal_obj_li_get_result,
log_lvl: log_lvl
// 1. FAST PATH: Check cache
if (try_cache) {
try {
const cached_li = await db_journals.journal.where('for_id').equals(for_obj_id).toArray();
if (cached_li && cached_li.length > 0) {
if (log_lvl) console.log(`JOURNAL LIST: Cache hit (${cached_li.length}).`);
_refresh_journal_li_background({
api_cfg, for_obj_type, for_obj_id, qry_person_id, inc_entry_li,
enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl: 0
});
if (log_lvl) {
console.log('Processed object list:', processed_obj_li);
}
// Save the updated results list to the database
if (log_lvl) {
console.log('Saving to DB...');
}
await db_save_ae_obj_li__ae_obj({
db_instance: db_journals,
table_name: 'journal',
obj_li: processed_obj_li,
properties_to_save: properties_to_save,
log_lvl: log_lvl
});
if (log_lvl) {
console.log('DB save completed.');
}
return cached_li;
}
return journal_obj_li_get_result;
} else {
return [];
}
} catch (e) {}
}
// 2. SLOW PATH: API
return await _refresh_journal_li_background({
api_cfg, for_obj_type, for_obj_id, qry_person_id, inc_entry_li,
enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl
});
}
if (log_lvl) {
console.log('ae_promises.load__journal_obj_li:', ae_promises.load__journal_obj_li);
async function _refresh_journal_li_background({ api_cfg, for_obj_type, for_obj_id, qry_person_id, inc_entry_li, enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl }: any) {
if (typeof navigator !== 'undefined' && !navigator.onLine) return [];
let promise;
if (qry_person_id) {
const search_query: any = { and: [{ field: 'person_id_random', op: 'eq', value: qry_person_id }] };
if (for_obj_id) search_query.and.push({ field: `${for_obj_type}_id_random`, op: 'eq', value: for_obj_id });
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: true });
if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: true });
promise = api.search_ae_obj_v3({ api_cfg, obj_type: 'journal', search_query, order_by_li, limit, offset, log_lvl });
} else {
promise = api.get_ae_obj_li_v3({ api_cfg, obj_type: 'journal', for_obj_type, for_obj_id, enabled, hidden, limit, offset, order_by_li, log_lvl });
}
if (inc_entry_li) {
// Load the entries for the journals
if (log_lvl) {
console.log(`Need to load the entry list for each journal now`);
}
for (let i = 0; i < ae_promises.load__journal_obj_li.length; i++) {
const journal_obj = ae_promises.load__journal_obj_li[i];
const journal_id = journal_obj.journal_id_random;
const load_journal_entry_obj_li = load_ae_obj_li__journal_entry({
api_cfg: api_cfg,
for_obj_type: 'journal',
for_obj_id: journal_id,
enabled: enabled,
hidden: hidden,
limit: limit,
offset: offset,
order_by_li: order_by_li,
params: params,
try_cache: try_cache,
log_lvl: log_lvl
}).then((journal_entry_obj_li) => {
if (log_lvl) {
console.log(`journal_entry_obj_li = `, journal_entry_obj_li);
}
return journal_entry_obj_li;
});
if (log_lvl) {
console.log(`load_journal_entry_obj_li = `, load_journal_entry_obj_li);
try {
const results = await promise;
if (results) {
if (try_cache) {
const processed = await process_ae_obj__journal_props({ obj_li: results, log_lvl });
await db_save_ae_obj_li__ae_obj({ db_instance: db_journals, table_name: 'journal', obj_li: processed, properties_to_save, log_lvl });
}
if (inc_entry_li) {
for (const journal of results) {
load_ae_obj_li__journal_entry({
api_cfg, for_obj_type: 'journal', for_obj_id: journal.journal_id_random,
enabled, hidden, limit, offset, order_by_li, params, try_cache, log_lvl: 0
});
}
}
return results;
}
}
return ae_promises.load__journal_obj_li;
} catch (e) {}
return [];
}
// Updated 2026-01-05
@@ -511,10 +424,6 @@ export async function qry__journal({
search_query.and.push({ field: 'start_datetime', op: 'gt', value: qry_start_datetime });
}
// if (qry_str && qry_str.length > 2) {
// // V3 Search full text handling would be added here if supported by backend via specific field or op
// }
// Add for_obj_id context (Account ID)
if (journal_id) {
// Assuming journal_id here is actually the account_id as per original usage context
@@ -582,156 +491,6 @@ export async function qry__journal({
return ae_promises.load__journal_obj_li;
}
// This function will loop through the journal_obj_li and save each one to the DB.
// Removed 2025-06-04
// export async function db_save_ae_obj_li__journal(
// {
// obj_type,
// obj_li,
// log_lvl = 0
// }: {
// obj_type: string,
// obj_li: any,
// log_lvl?: number
// }
// ) {
// if (log_lvl) {
// console.log(`*** db_save_ae_obj_li__journal() *** obj_type=${obj_type}`, obj_li);
// }
// if (obj_li && obj_li.length) {
// let obj_li_id: string[] = [];
// for (const obj of obj_li) {
// // obj_li.forEach(async function (obj: any) {
// if (log_lvl) {
// console.log(`Processing ae_obj ${obj_type}:`, obj);
// }
// let description = obj.description ?? '';
// // remove the most common zerowidth characters from the start of the file
// let description_cleaned: string = description.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
// let description_md_html: null|string = await marked.parse(description_cleaned ?? '') ?? null;
// // let description_md_html_alt: null|string = await marked.parse(description_cleaned ?? '', { gfm: false }) ?? null;
// let obj_record = {
// id: obj.journal_id_random,
// journal_id: obj.journal_id_random,
// code: obj.code,
// for_type: obj.for_type,
// for_id: obj.for_id,
// type_code: obj.type_code,
// account_id: obj.account_id_random,
// person_id: obj.person_id_random,
// name: obj.name,
// short_name: obj.short_name,
// summary: obj.summary,
// outline: obj.outline,
// description: obj.description,
// description_md_html: description_md_html, // Use the markdown parser to generate HTML
// description_html: obj.description_html,
// description_json: obj.description_json,
// // start_datetime: obj.start_datetime,
// // end_datetime: obj.end_datetime,
// timezone: obj.timezone,
// alert: obj.alert,
// alert_msg: obj.alert_msg,
// sort_by: obj.sort_by,
// sort_by_desc: obj.sort_by_desc,
// cfg_json: obj.cfg_json ?? {},
// data_json: obj.data_json ?? {},
// // ux_mode: obj.ux_mode,
// // This only allows for basic access to the data.
// passcode_read: obj.passcode_read, // For LLM (AI) generated summary...???
// passcode_read_expire: obj.passcode_read_expire,
// passcode_write: obj.passcode_write,
// passcode_write_expire: obj.passcode_write_expire,
// passcode: obj.passcode, // For Journal Entry encryption password
// passcode_timeout: obj.passcode_timeout,
// private_passcode: obj.private_passcode, // Combine with Journal passcode to encrypt and decrypt Entries
// auth_key: obj.auth_key, // For Journal authorization without sign in
// enable: obj.enable,
// hide: obj.hide,
// priority: obj.priority,
// sort: obj.sort,
// group: obj.group,
// notes: obj.notes,
// created_on: obj.created_on,
// updated_on: obj.updated_on,
// // Generated fields for sorting locally only
// tmp_sort_1: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.updated_on}_${obj.created_on}`,
// tmp_sort_2: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.updated_on ?? obj.created_on}`,
// tmp_sort_3: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.name}_${obj.updated_on ?? obj.created_on}`,
// // tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
// // tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
// combined_passcode: `${obj.passcode}:${obj.private_passcode ?? ''}`, // Combined Journal passcode and Journal private passcode to encrypt and decrypt Entries
// // From SQL view
// journal_entry_count: obj.journal_entry_count,
// // A key value list of the others
// // journal_other_kv: obj.journal_other_kv,
// // journal_other_li: obj.journal_other_li,
// };
// let id_random = null;
// try {
// id_random = await db_journals.journal.update(obj_record.id, obj_record);
// } catch (error) {
// console.log(`Error: Failed to update ${obj_record.id}: ${error}`);
// }
// if (!id_random) {
// if (log_lvl) {
// console.log(`Failed to update record with ID: ${obj_record.id}. Trying put...`);
// }
// try {
// id_random = await db_journals.journal.put(obj_record);
// } catch (error) {
// console.log(`Error: Failed to put ${obj.journal_id_random}: ${error}`);
// }
// } else {
// if (log_lvl) {
// console.log(`Updated record with ID: ${obj_record.id}`);
// }
// }
// if (!id_random) {
// console.log(`Failed to save record with ID: ${obj_record.id}`);
// } else {
// if (log_lvl) {
// console.log(`Saved record with ID: ${obj_record.id}`);
// }
// }
// }
// return obj_li_id;
// } else {
// if (log_lvl) {
// console.log('No objects to save.');
// }
// return [];
// }
// }
// Updated 2025-05-09
const properties_to_save = [
'id',
@@ -910,4 +669,4 @@ export async function process_ae_obj__journal_props({
return obj;
}
});
}
}

View File

@@ -1345,6 +1345,17 @@ email = ${$ae_loc?.email}
</div>
</div>
{/if}
{:else if browser && $ae_loc && $ae_loc.allow_access === null}
<!-- NEW: Render layout shell while waiting for initial access check -->
<div class="min-h-screen flex items-center justify-center">
<div class="flex flex-col items-center gap-4">
<span class="fas fa-cog fa-spin text-4xl text-primary-500"></span>
<div class="text-center">
<div class="text-lg font-bold">Initializing Layout...</div>
<div class="text-xs opacity-60">Determining access context</div>
</div>
</div>
</div>
{:else if browser || flag_reload}
<div
class="flex flex-col items-center justify-center max-w-lg mx-auto space-y-6 border border-red-500 rounded-lg p-4 bg-green-50 dark:bg-green-900 m-8"

View File

@@ -4,8 +4,6 @@ console.log(`ae_l_journals [journal_id] +layout.ts start`);
import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
// import { db_journals, journal_field_li, journal_entry_field_li } from "$lib/ae_journals/db_journals";
// import { load_ae_obj_id } from '$lib/ae_core/core__crud_generic';
export async function load({ params, parent }) {
const log_lvl: number = 0;
@@ -36,59 +34,23 @@ export async function load({ params, parent }) {
});
}
ae_acct.slct.journal_id = journal_id;
if (log_lvl) {
console.log(`ae_journals journals [journal_id] +page.ts: journal_id = `, journal_id);
}
if (browser) {
if (log_lvl) {
console.log(`ae_journals journals [journal_id] +page.ts: journal_id = `, journal_id);
}
// Load journal object
// let load_journal_obj = load_ae_obj_id({
// api_cfg: ae_acct.api,
// obj_type: 'journal',
// obj_id: journal_id,
// db_instance: db_journals,
// db_field_li: journal_field_li,
// inc_obj_type_li: ['journal_entry'],
// log_lvl: 2
// });
if (log_lvl) console.log(`ae_journals journals [journal_id] +layout.ts (Non-Blocking)`);
const load_journal_obj = await journals_func.load_ae_obj_id__journal({
// OPTIMIZATION: Fire the journal load in the background.
// The journal module now uses SWR, and components watching IDB
// will update automatically when the refresh completes.
journals_func.load_ae_obj_id__journal({
api_cfg: ae_acct.api,
journal_id: journal_id,
inc_entry_li: true,
enabled: 'enabled',
hidden: 'all', // 'not_hidden' to load only visible entries
hidden: 'all',
limit: 99,
try_cache: true,
log_lvl: log_lvl
log_lvl: 0
});
// .then((results) => {
// if (!results) {
// error(404, {
// message: 'Journals - Journal not found'
// });
// } else {
// // ae_acct.slct.journal_obj = results;
// }
// })
// .catch((err) => {
// console.error(`Error loading journal object:`, err);
// error(500, {
// message: 'Journals - Error loading journal object'
// });
// });
if (!load_journal_obj) {
console.warn(`ae Journals [journal_id] +layout.ts: Journal ${journal_id} not found via API or Cache.`);
// error(404, {
// message: 'Journals - Journal Entry not found'
// });
} else {
ae_acct.slct.load_journal_obj = load_journal_obj;
}
}
// WARNING: Precaution against shared data between sites.