perf(hydration): implement cache-first site discovery and SWR event loading
- Refactored lookup_site_domain_v3 to be cache-first, unblocking root layout. - Implemented Stale-While-Revalidate (SWR) pattern for load_ae_obj_id__event. - Added global hydration overlay and loading spinner to +layout.svelte. - Updated site domain whitelist to persist critical account/styling metadata in Dexie. - Refactored root load function to return immediately if cached site data is found.
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -55,5 +55,5 @@ This is a list of tasks to be completed before the next event/show/conference.
|
|||||||
- [x] Phase 3: Standardize snake_case bridge and OS command set.
|
- [x] Phase 3: Standardize snake_case bridge and OS command set.
|
||||||
- [x] Phase 4: Implement device heartbeat and telemetry loop (Verified UTC timestamps).
|
- [x] Phase 4: Implement device heartbeat and telemetry loop (Verified UTC timestamps).
|
||||||
- [ ] [IN PROGRESS] Move heartbeat/sync visibility from "Secret Monitor" to Launcher Config Drawer.
|
- [ ] [IN PROGRESS] Move heartbeat/sync visibility from "Secret Monitor" to Launcher Config Drawer.
|
||||||
- [ ] Phase 5: Implement specialized AppleScript handlers for Office/Keynote.
|
- [x] Phase 5: Implement specialized AppleScript handlers for Office/Keynote. (Completed 2026-01-26)
|
||||||
- [ ] [REFINEMENT] Build Telemetry Dashboard in Launcher Config.
|
- [ ] [REFINEMENT] Build Telemetry Dashboard in Launcher Config.
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export async function lookup_site_domain({
|
|||||||
} as any;
|
} as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updated 2026-01-07
|
// Updated 2026-01-26 (Cache-First Optimization)
|
||||||
export async function lookup_site_domain_v3({
|
export async function lookup_site_domain_v3({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
fqdn,
|
fqdn,
|
||||||
@@ -104,12 +104,34 @@ export async function lookup_site_domain_v3({
|
|||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}): Promise<ae_SiteDomain | null> {
|
}): Promise<ae_SiteDomain | null> {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`*** lookup_site_domain_v3() *** fqdn=${fqdn}`);
|
console.log(`*** lookup_site_domain_v3() *** fqdn=${fqdn} (Cache-First)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. FAST PATH: Check local cache first
|
||||||
|
let cached = null;
|
||||||
|
try {
|
||||||
|
cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||||
|
if (cached) {
|
||||||
|
if (log_lvl) console.log('BOOTSTRAP: Cache hit. Returning cached site domain immediately.');
|
||||||
|
|
||||||
|
// Trigger background refresh to keep cache fresh, but don't await it
|
||||||
|
_refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl: 0 });
|
||||||
|
|
||||||
|
return cached as any;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('BOOTSTRAP: Cache read failed.', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. SLOW PATH: Wait for API if cache is empty
|
||||||
|
return await _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper to perform the actual API fetch and cache update
|
||||||
|
*/
|
||||||
|
async function _refresh_site_domain_v3_background({ api_cfg, fqdn, view, log_lvl }: any) {
|
||||||
try {
|
try {
|
||||||
// CRITICAL: For the unauthenticated Bootstrap lookup, we must NOT send
|
|
||||||
// any existing auth tokens or account IDs that might be in the global config.
|
|
||||||
const guest_api_cfg = { ...api_cfg };
|
const guest_api_cfg = { ...api_cfg };
|
||||||
guest_api_cfg.headers = { ...api_cfg.headers };
|
guest_api_cfg.headers = { ...api_cfg.headers };
|
||||||
|
|
||||||
@@ -134,30 +156,19 @@ export async function lookup_site_domain_v3({
|
|||||||
and: [{ field: 'fqdn', op: 'eq', value: fqdn }]
|
and: [{ field: 'fqdn', op: 'eq', value: fqdn }]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (log_lvl) {
|
|
||||||
console.log(`BOOTSTRAP SEARCH: fqdn=${fqdn}`);
|
|
||||||
console.log(`BOOTSTRAP HEADERS:`, guest_api_cfg.headers);
|
|
||||||
console.log(`BOOTSTRAP QUERY:`, JSON.stringify(search_query));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use search because we are looking up by a unique field (fqdn) rather than ID.
|
|
||||||
// The backend should return a list, but since FQDN is unique, it will have 1 item.
|
|
||||||
const result_li = await api.search_ae_obj_v3({
|
const result_li = await api.search_ae_obj_v3({
|
||||||
api_cfg: guest_api_cfg,
|
api_cfg: guest_api_cfg,
|
||||||
obj_type: 'site_domain',
|
obj_type: 'site_domain',
|
||||||
search_query,
|
search_query,
|
||||||
view, // This view should ideally join with site and account for the root lookup
|
view,
|
||||||
enabled: 'enabled',
|
enabled: 'enabled',
|
||||||
hidden: 'all',
|
hidden: 'all',
|
||||||
limit: 1,
|
limit: 1,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
});
|
||||||
|
|
||||||
if (log_lvl) console.log(`BOOTSTRAP RESULT:`, result_li);
|
|
||||||
|
|
||||||
if (result_li && result_li.length > 0) {
|
if (result_li && result_li.length > 0) {
|
||||||
const result = result_li[0];
|
const result = result_li[0];
|
||||||
// Standardize and save to cache
|
|
||||||
const processed_obj_li = await process_ae_obj__site_domain_props({
|
const processed_obj_li = await process_ae_obj__site_domain_props({
|
||||||
obj_li: [result],
|
obj_li: [result],
|
||||||
log_lvl
|
log_lvl
|
||||||
@@ -172,12 +183,9 @@ export async function lookup_site_domain_v3({
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log('Site domain lookup V3 failed.', error);
|
if (log_lvl) console.log('Site domain refresh V3 failed.', error);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
if (log_lvl) console.log('Attempting to load site domain from local cache (V3 fallback)...');
|
|
||||||
const cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
|
||||||
return (cached as any) || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function load_ae_obj_id__site({
|
export async function load_ae_obj_id__site({
|
||||||
@@ -661,6 +669,10 @@ const properties_to_save__site_domain = [
|
|||||||
'site_domain_id_random',
|
'site_domain_id_random',
|
||||||
'site_id',
|
'site_id',
|
||||||
'site_id_random',
|
'site_id_random',
|
||||||
|
'account_id',
|
||||||
|
'account_id_random',
|
||||||
|
'account_code',
|
||||||
|
'account_name',
|
||||||
'fqdn',
|
'fqdn',
|
||||||
'access_key',
|
'access_key',
|
||||||
'enable',
|
'enable',
|
||||||
@@ -671,6 +683,11 @@ const properties_to_save__site_domain = [
|
|||||||
'sort',
|
'sort',
|
||||||
'group',
|
'group',
|
||||||
'notes',
|
'notes',
|
||||||
|
'header_image_path',
|
||||||
|
'style_href',
|
||||||
|
'google_tracking_id',
|
||||||
|
'access_code_kv_json',
|
||||||
|
'cfg_json',
|
||||||
'created_on',
|
'created_on',
|
||||||
'updated_on',
|
'updated_on',
|
||||||
'tmp_sort_1',
|
'tmp_sort_1',
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { load_ae_obj_li__event_file } from '$lib/ae_events/ae_events__event_file
|
|||||||
|
|
||||||
const ae_promises: key_val = {};
|
const ae_promises: key_val = {};
|
||||||
|
|
||||||
// Updated 2026-01-16
|
// Updated 2026-01-26 (Stale-While-Revalidate Optimization)
|
||||||
export async function load_ae_obj_id__event({
|
export async function load_ae_obj_id__event({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
event_id,
|
event_id,
|
||||||
@@ -39,16 +39,54 @@ export async function load_ae_obj_id__event({
|
|||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}): Promise<ae_Event | null> {
|
}): Promise<ae_Event | null> {
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`*** load_ae_obj_id__event() *** event_id=${event_id}`);
|
console.log(`*** load_ae_obj_id__event() *** event_id=${event_id} (SWR Optimization)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cached_event: any = null;
|
||||||
|
|
||||||
|
// 1. FAST PATH: Return cached data immediately
|
||||||
|
if (try_cache) {
|
||||||
|
try {
|
||||||
|
cached_event = await db_events.event.get(event_id);
|
||||||
|
if (cached_event) {
|
||||||
|
if (log_lvl) console.log('EVENT LOAD: Cache hit. Returning stale data immediately.');
|
||||||
|
|
||||||
|
// Trigger background refresh
|
||||||
|
_refresh_event_v3_background({
|
||||||
|
api_cfg, event_id, view, try_cache,
|
||||||
|
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li,
|
||||||
|
log_lvl: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Still handle nested loads for the cached version to ensure UI richness
|
||||||
|
return await _handle_nested_loads(cached_event, {
|
||||||
|
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (log_lvl) console.warn('EVENT LOAD: Cache read failed.', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. SLOW PATH: Wait for API if cache is empty or try_cache is false
|
||||||
|
return await _refresh_event_v3_background({
|
||||||
|
api_cfg, event_id, view, try_cache,
|
||||||
|
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li,
|
||||||
|
log_lvl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper to perform the actual API fetch and cache update for events
|
||||||
|
*/
|
||||||
|
async function _refresh_event_v3_background({
|
||||||
|
api_cfg, event_id, view, try_cache,
|
||||||
|
inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li,
|
||||||
|
log_lvl
|
||||||
|
}: any) {
|
||||||
// Check if offline
|
// Check if offline
|
||||||
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
if (typeof navigator !== 'undefined' && !navigator.onLine) {
|
||||||
if (log_lvl) console.log('Browser is offline. Skipping API and attempting cache load.');
|
if (log_lvl) console.log('Browser is offline. Skipping API refresh.');
|
||||||
ae_promises.load__event_obj = await db_events.event.get(event_id);
|
|
||||||
if (ae_promises.load__event_obj) {
|
|
||||||
return await _handle_nested_loads(ae_promises.load__event_obj, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl });
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,31 +113,15 @@ export async function load_ae_obj_id__event({
|
|||||||
log_lvl: log_lvl
|
log_lvl: log_lvl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ae_promises.load__event_obj = event_obj_get_result;
|
|
||||||
} else {
|
return await _handle_nested_loads(event_obj_get_result, {
|
||||||
console.log('No results returned from API.');
|
api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl
|
||||||
if (try_cache) {
|
});
|
||||||
if (log_lvl) console.log('Attempting to load from local cache...');
|
|
||||||
ae_promises.load__event_obj = await db_events.event.get(event_id);
|
|
||||||
} else {
|
|
||||||
ae_promises.load__event_obj = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log('API request failed.', error);
|
if (log_lvl) console.log('Event API refresh failed.', error);
|
||||||
if (try_cache) {
|
|
||||||
if (log_lvl) console.log('Attempting to load from local cache after error...');
|
|
||||||
ae_promises.load__event_obj = await db_events.event.get(event_id);
|
|
||||||
} else {
|
|
||||||
ae_promises.load__event_obj = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
if (!ae_promises?.load__event_obj) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _handle_nested_loads(ae_promises.load__event_obj, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,48 +130,53 @@ export async function load_ae_obj_id__event({
|
|||||||
async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl }: any) {
|
async function _handle_nested_loads(event_obj: any, { api_cfg, inc_device_li, inc_file_li, inc_location_li, inc_session_li, inc_template_li, log_lvl }: any) {
|
||||||
const current_event_id = event_obj.event_id_random || event_obj.event_id || event_obj.id;
|
const current_event_id = event_obj.event_id_random || event_obj.event_id || event_obj.id;
|
||||||
|
|
||||||
|
// Use Promise.all for concurrent nested loads to further reduce delay
|
||||||
|
const tasks = [];
|
||||||
|
|
||||||
if (inc_device_li) {
|
if (inc_device_li) {
|
||||||
event_obj.event_device_obj_li = await load_ae_obj_li__event_device({
|
tasks.push(load_ae_obj_li__event_device({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
for_obj_type: 'event',
|
for_obj_type: 'event',
|
||||||
for_obj_id: current_event_id,
|
for_obj_id: current_event_id,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
}).then(res => event_obj.event_device_obj_li = res));
|
||||||
}
|
}
|
||||||
if (inc_file_li) {
|
if (inc_file_li) {
|
||||||
event_obj.event_file_li = await load_ae_obj_li__event_file({
|
tasks.push(load_ae_obj_li__event_file({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
for_obj_type: 'event',
|
for_obj_type: 'event',
|
||||||
for_obj_id: current_event_id,
|
for_obj_id: current_event_id,
|
||||||
enabled: 'all',
|
enabled: 'all',
|
||||||
limit: 100,
|
limit: 100,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
}).then(res => event_obj.event_file_li = res));
|
||||||
}
|
}
|
||||||
if (inc_location_li) {
|
if (inc_location_li) {
|
||||||
event_obj.event_location_obj_li = await load_ae_obj_li__event_location({
|
tasks.push(load_ae_obj_li__event_location({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
for_obj_type: 'event',
|
for_obj_type: 'event',
|
||||||
for_obj_id: current_event_id,
|
for_obj_id: current_event_id,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
}).then(res => event_obj.event_location_obj_li = res));
|
||||||
}
|
}
|
||||||
if (inc_session_li) {
|
if (inc_session_li) {
|
||||||
event_obj.event_session_obj_li = await load_ae_obj_li__event_session({
|
tasks.push(load_ae_obj_li__event_session({
|
||||||
api_cfg,
|
api_cfg,
|
||||||
for_obj_type: 'event',
|
for_obj_type: 'event',
|
||||||
for_obj_id: current_event_id,
|
for_obj_id: current_event_id,
|
||||||
log_lvl
|
log_lvl
|
||||||
});
|
}).then(res => event_obj.event_session_obj_li = res));
|
||||||
}
|
}
|
||||||
if (inc_template_li) {
|
if (inc_template_li) {
|
||||||
event_obj.event_badge_template_obj_li =
|
tasks.push(load_ae_obj_li__event_badge_template({
|
||||||
await load_ae_obj_li__event_badge_template({
|
api_cfg,
|
||||||
api_cfg,
|
event_id: current_event_id,
|
||||||
event_id: current_event_id,
|
log_lvl
|
||||||
log_lvl
|
}).then(res => event_obj.event_badge_template_obj_li = res));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tasks.length > 0) await Promise.all(tasks);
|
||||||
|
|
||||||
return event_obj;
|
return event_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -718,6 +718,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_hydrating = $state(true);
|
||||||
|
$effect(() => {
|
||||||
|
if (browser && $ae_loc?.account_id) {
|
||||||
|
// Give a tiny delay to ensure everything is rendered
|
||||||
|
setTimeout(() => is_hydrating = false, 150);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (browser) {
|
if (browser) {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -1325,6 +1333,18 @@ email = ${$ae_loc?.email}
|
|||||||
|
|
||||||
<!-- The children will contain top level (directly under body tag) div tag(s) or similar. Modal and AppShell should also be set there. The AppShell gives access to a header, footer, AppBar (usually under header), -->
|
<!-- The children will contain top level (directly under body tag) div tag(s) or similar. Modal and AppShell should also be set there. The AppShell gives access to a header, footer, AppBar (usually under header), -->
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
||||||
|
{#if is_hydrating}
|
||||||
|
<div class="fixed inset-0 z-[99] flex flex-col items-center justify-center bg-surface-50/50 dark:bg-surface-900/50 backdrop-blur-[2px] transition-all duration-500">
|
||||||
|
<div class="preset-filled-surface-100-900 p-6 rounded-xl shadow-2xl border border-surface-500/20 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">Hydrating Aether...</div>
|
||||||
|
<div class="text-xs opacity-60">Synchronizing local cache</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{:else if browser || flag_reload}
|
{:else if browser || flag_reload}
|
||||||
<div
|
<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"
|
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"
|
||||||
|
|||||||
@@ -123,6 +123,22 @@ export async function load({ fetch, params, parent, route, url }) {
|
|||||||
let api_error = false;
|
let api_error = false;
|
||||||
let is_native = false;
|
let is_native = false;
|
||||||
|
|
||||||
|
// 1. FAST PATH: Check Dexie for cached site domain first
|
||||||
|
// This allows the load function to return nearly instantly if the user has visited before.
|
||||||
|
if (typeof window !== 'undefined' && (window as any).indexedDB) {
|
||||||
|
try {
|
||||||
|
// Use the db_core instance directly for a quick lookup
|
||||||
|
const { db_core } = await import('$lib/ae_core/db_core');
|
||||||
|
const cached = await db_core.site_domain.where('fqdn').equals(fqdn).first();
|
||||||
|
if (cached) {
|
||||||
|
if (log_lvl) console.log('ROOT LOAD: Found cached site domain. Unblocking layout.');
|
||||||
|
result = cached;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (log_lvl) console.warn('ROOT LOAD: Failed to read from Dexie cache.', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Detect Aether Native Bridge (Electron)
|
// Detect Aether Native Bridge (Electron)
|
||||||
if (typeof window !== 'undefined' && (window as any).aetherNative) {
|
if (typeof window !== 'undefined' && (window as any).aetherNative) {
|
||||||
is_native = true;
|
is_native = true;
|
||||||
@@ -132,6 +148,7 @@ export async function load({ fetch, params, parent, route, url }) {
|
|||||||
if (native_device_config) {
|
if (native_device_config) {
|
||||||
if (log_lvl) console.log('ROOT LOAD: Native device config received:', native_device_config);
|
if (log_lvl) console.log('ROOT LOAD: Native device config received:', native_device_config);
|
||||||
// Map native device config to the expected result structure
|
// Map native device config to the expected result structure
|
||||||
|
// Use native as source of truth if available
|
||||||
result = {
|
result = {
|
||||||
...native_device_config,
|
...native_device_config,
|
||||||
// Ensure naming consistency
|
// Ensure naming consistency
|
||||||
@@ -179,10 +196,10 @@ export async function load({ fetch, params, parent, route, url }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform standard FQDN lookup if not native or if native fetch failed
|
// 2. SLOW PATH: Wait for API site lookup only if we have no result yet
|
||||||
if (!result) {
|
if (!result) {
|
||||||
try {
|
try {
|
||||||
if (log_lvl) console.log(`ROOT LOAD: Starting site lookup V3 for ${fqdn}...`);
|
if (log_lvl) console.log(`ROOT LOAD: No cache. Starting site lookup V3 for ${fqdn}...`);
|
||||||
|
|
||||||
// Use dedicated Agent Key for Bootstrap and include the unauthenticated bypass header ONLY for this request
|
// Use dedicated Agent Key for Bootstrap and include the unauthenticated bypass header ONLY for this request
|
||||||
const bootstrap_api_cfg = {
|
const bootstrap_api_cfg = {
|
||||||
@@ -206,6 +223,15 @@ export async function load({ fetch, params, parent, route, url }) {
|
|||||||
console.error(`ROOT LOAD: Site lookup critical failure for ${fqdn}.`, err);
|
console.error(`ROOT LOAD: Site lookup critical failure for ${fqdn}.`, err);
|
||||||
api_error = true;
|
api_error = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// We have a result (cache or native), fire off the refresh in the background to update Dexie
|
||||||
|
if (log_lvl) console.log('ROOT LOAD: Result already obtained. Background refresh triggered.');
|
||||||
|
lookup_site_domain_v3({
|
||||||
|
api_cfg: ae_api_init,
|
||||||
|
fqdn,
|
||||||
|
view: 'base',
|
||||||
|
log_lvl: 0
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defensive check: if result is false (common from API helper) or null, use emergency ghost
|
// Defensive check: if result is false (common from API helper) or null, use emergency ghost
|
||||||
|
|||||||
Reference in New Issue
Block a user