fix(idaa): harden data sync against padStart crashes and fix Archive Content sort

- Hardened object processors in Archives and Posts modules to safely handle null 'sort' values, preventing runtime TypeErrors during data synchronization.
- Fixed inconsistent sorting in Archive Content list by correctly implementing descending order (sort then reverse) and adding a configuration loading guard to the liveQuery.
- Standardized safe data processing patterns in SVELTE_DEXIE_GUIDE.md.
- Performed minor cleanup and visibility logic hardening in Recovery Meetings module.
This commit is contained in:
Scott Idem
2026-02-05 12:27:26 -05:00
parent bd39fd3061
commit 2306f2d0c4
9 changed files with 104 additions and 63 deletions

View File

@@ -99,11 +99,11 @@
let results = await db_archives.content
.where('archive_id')
.equals($idaa_slct?.archive_id ?? '') // null or undefined does not reset things like '' does
.reverse()
// .reverse() // Incorrect usage: reverse() on collection is ignored by sortBy()
.sortBy('tmp_sort_2');
// .sortBy('updated_on');
return results;
return results.reverse();
} else {
let results = await db_archives.content
.where('archive_id')

View File

@@ -1974,6 +1974,7 @@
preset-tonal-surface hover:preset-filled-surface-100-900
form-control col-6
"
class:required={!$lq__event_obj?.timezone && !$ae_loc?.current_timezone}
>
<option value="" class:hidden={!$ae_loc.trusted_access}
>-- None --</option

View File

@@ -25,7 +25,6 @@
// *** Import Aether specific variables and functions
import { ae_util } from '$lib/ae_utils/ae_utils';
import { core_func } from '$lib/ae_core/ae_core_functions';
import {
ae_snip,
ae_loc,
@@ -43,23 +42,23 @@
let visible_event_obj_li = $derived((() => {
// Subscribe to the LiveQuery observable using $ prefix
const list = $lq__event_obj_li;
if (!list || !Array.isArray(list)) {
if (log_lvl > 1) console.log('visible_event_obj_li: Waiting for data stream...');
return [];
}
const filtered = list.filter((item: any) => {
if (!item) return false;
// ADMIN/TRUSTED: See everything
if ($ae_loc.trusted_access) return true;
// PUBLIC: Filter hidden/disabled
// Safely handle null/undefined fields by assuming visible/enabled (permissive default)
const is_hidden = item.hide === true || item.hide === 1;
const is_disabled = item.enable === false || item.enable === 0; // Only block if explicitly false/0
return !is_hidden && !is_disabled;
});
@@ -74,49 +73,49 @@
}
}
function add_activity_log({
action = 'idaa_meetings_page',
action_with = 'none'
}: {
action?: string;
action_with?: string;
}) {
let last_cache_refresh_iso = new Date($ae_loc?.last_cache_refresh);
// function add_activity_log({
// action = 'idaa_meetings_page',
// action_with = 'none'
// }: {
// action?: string;
// action_with?: string;
// }) {
// let last_cache_refresh_iso = new Date($ae_loc?.last_cache_refresh);
let activity_description = `
${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? 'no-email'}
allow=${$ae_loc?.allow_access}
last_cache_refresh=${last_cache_refresh_iso.toLocaleString()}
data_route=${data?.route?.toString() ?? 'unknown'}
`;
// let activity_description = `
// ${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? 'no-email'}
// allow=${$ae_loc?.allow_access}
// last_cache_refresh=${last_cache_refresh_iso.toLocaleString()}
// data_route=${data?.route?.toString() ?? 'unknown'}
// `;
let data_kv = {
external_client_id: data?.route.id,
name: `IDAA: ${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? ''}`,
description: activity_description ?? null,
object_type: 'event', // archive, post, event
url_root: data?.url.origin,
url_full_path: data?.url.pathname,
url_params: data?.url.searchParams.toString(),
action: action,
action_with: action_with ?? 'none',
meta_json: {
allow_access: $ae_loc?.allow_access,
last_cache_refresh: $ae_loc?.last_cache_refresh,
last_cache_refresh_iso: last_cache_refresh_iso.toISOString(),
last_cache_refresh_locale: last_cache_refresh_iso.toLocaleString(),
access_level: $ae_loc?.access_level,
iframe: $ae_loc?.iframe
}
};
// let data_kv = {
// external_client_id: data?.route.id,
// name: `IDAA: ${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? ''}`,
// description: activity_description ?? null,
// object_type: 'event', // archive, post, event
// url_root: data?.url.origin,
// url_full_path: data?.url.pathname,
// url_params: data?.url.searchParams.toString(),
// action: action,
// action_with: action_with ?? 'none',
// meta_json: {
// allow_access: $ae_loc?.allow_access,
// last_cache_refresh: $ae_loc?.last_cache_refresh,
// last_cache_refresh_iso: last_cache_refresh_iso.toISOString(),
// last_cache_refresh_locale: last_cache_refresh_iso.toLocaleString(),
// access_level: $ae_loc?.access_level,
// iframe: $ae_loc?.iframe
// }
// };
core_func.create_ae_obj__activity_log({
api_cfg: $ae_api,
account_id: $ae_loc.account_id,
data_kv: data_kv,
log_lvl: log_lvl
});
}
// core_func.create_ae_obj__activity_log({
// api_cfg: $ae_api,
// account_id: $ae_loc.account_id,
// data_kv: data_kv,
// log_lvl: log_lvl
// });
// }
</script>
<section
@@ -142,11 +141,12 @@
{#each visible_event_obj_li as idaa_event_obj, index}
{#if idaa_event_obj}
<!-- NOTE: There is extra logic here to hide items that are hidden/disabled for public users, but still show them to trusted/admin users. This is because the LiveQuery returns all items matching the query, and we need to filter on the client side based on the user's access level. The `visible_event_obj_li` derived store handles this filtering logic, so we can safely render based on that list. -->
<div
class="container recovery_meeting event_obj border rounded-lg p-2 mb-2 space-y-2 w-full max-w-(--breakpoint-lg) flex flex-col items-center justify-center bg-white"
class:hidden={(idaa_event_obj?.hide || !idaa_event_obj?.enable) &&
!$ae_loc.trusted_access}
class:dim={idaa_event_obj?.hide}
!($ae_loc.trusted_access && $ae_loc.edit_mode)}
class:dim={idaa_event_obj?.hide || !idaa_event_obj?.enable}
class:bg-warning-100={!idaa_event_obj?.enable}
class:text-warning-900={!idaa_event_obj?.enable}
>

View File

@@ -8,7 +8,6 @@
// *** Import Svelte specific
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
// *** Import other supporting libraries
@@ -397,7 +396,8 @@
onclick={() => {
$idaa_loc.recovery_meetings.qry__hidden = 'all';
$idaa_loc.recovery_meetings.qry__enabled = 'all';
$idaa_loc.recovery_meetings.qry__limit = 500;
// NOTE: I may re-enable this limit reset later. It is sometimes useful.
// $idaa_loc.recovery_meetings.qry__limit = 500;
}}
class="
novi_btn btn_show_recovery_mtg_event ae_btn btn-warning
@@ -408,7 +408,7 @@
>
<span class="fas fa-eye m-1"></span> Show Disabled Events
</button>
{:else if $ae_loc.administrator_access && $idaa_loc.recovery_meetings.qry__enabled != 'enabled'}
{:else if $ae_loc.edit_mode && $ae_loc.administrator_access && $idaa_loc.recovery_meetings.qry__enabled != 'enabled'}
<button type="button"
onclick={() => {
$idaa_loc.recovery_meetings.qry__enabled = 'enabled';