diff --git a/documentation/TODO__Agents.md b/documentation/TODO__Agents.md index 55cf3ef2..e3aae628 100644 --- a/documentation/TODO__Agents.md +++ b/documentation/TODO__Agents.md @@ -115,6 +115,34 @@ reactivity — only effects that actually read a changed field re-run. --- +### [Stores] IDB Content Version System (post June 10) +Scaffold added to `store_versions.ts` (`IDB_CONTENT_VERSIONS` constant) — values defined but +**not yet wired**. The mechanism mirrors `AE_LOC_VERSION` but targets Dexie table contents +rather than localStorage keys. + +**Why:** `db_save_ae_obj_li__ae_obj` uses a `properties_to_save` whitelist. When that whitelist +changes (e.g. adding/removing a stored field), existing cached IDB records are stale but never +automatically cleared. Users see the old shape until a record is individually refreshed. + +**How it will work:** +- Each `db_*.ts` calls a helper (`core__idb_dexie.ts`) on open that checks a `_meta` IDB table +- If stored version ≠ `IDB_CONTENT_VERSIONS[module][table]`, clear the table + update `_meta` +- SWR repopulates from API on next access (same as any cold-start) +- Bump a table's version in `IDB_CONTENT_VERSIONS` when `properties_to_save` changes shape + +**IDAA consideration:** +IDAA tables are already cleared by `indexedDB.deleteDatabase()` on sign-out/auth failure in +`(idaa)/+layout.svelte`. The content version check is a *complementary* deploy-time reset, not +a replacement. When wiring IDAA tables, ensure: (a) the check only runs on IDB open, not +mid-session; (b) the `_meta` table is included in the `deleteDatabase()` wipe scope. + +**Tasks:** +- [x] Write `check_and_clear_idb_tables()` helper in `core__idb_dexie.ts` (2026-05-14) +- [x] Wire helper into `db_journals.ts` (pilot — `journal_entry: 2` clears stale content_md_html on first load) (2026-05-14) +- [ ] Roll out to `db_events.ts`, `db_core.ts` +- [ ] Roll out to IDAA modules (`db_posts.ts`, `db_archives.ts`) — verify auth-wipe interaction first +- [ ] Update `store_versions.ts` comment from "NOT YET ACTIVE" to document the active mechanism + ### [Stores] Refactor — Phase 2c (deferred) Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining: diff --git a/src/lib/ae_core/core__idb_dexie.ts b/src/lib/ae_core/core__idb_dexie.ts index ffb8199e..5011bd21 100644 --- a/src/lib/ae_core/core__idb_dexie.ts +++ b/src/lib/ae_core/core__idb_dexie.ts @@ -1,6 +1,53 @@ import type { Dexie, Table } from 'dexie'; import { browser } from '$app/environment'; +/** + * Checks IDB table content versions and clears any tables whose version has + * changed since the last check. + * + * Version numbers live in IDB_CONTENT_VERSIONS (store_versions.ts). State is + * tracked in localStorage (ae_idb_ver__{module}__{table}) so each table is + * cleared exactly once per version bump, not on every page load. + * + * Call once at module init in each db_*.ts, after the singleton is created. + * The clear is intentionally fire-and-forget — the SWR pattern repopulates + * from the API naturally after a cache miss. + * + * A null stored version (never tracked) is treated as outdated so existing + * installs with stale cached data are cleaned up on first run. + */ +export async function check_and_clear_idb_tables({ + db_instance, + module_name, + table_versions, + log_lvl = 0 +}: { + db_instance: Dexie; + module_name: string; + table_versions: Record; + log_lvl?: number; +}): Promise { + if (!browser) return; + + for (const [table_name, expected_version] of Object.entries(table_versions)) { + const ls_key = `ae_idb_ver__${module_name}__${table_name}`; + const stored_raw = localStorage.getItem(ls_key); + const stored_version = stored_raw !== null ? parseInt(stored_raw, 10) : null; + + if (stored_version === expected_version) continue; + + try { + await db_instance.table(table_name).clear(); + localStorage.setItem(ls_key, String(expected_version)); + console.log( + `[IDB] "${module_name}.${table_name}" cleared — v${stored_version ?? 'new'} → v${expected_version}` + ); + } catch (e) { + console.warn(`[IDB] Failed to clear "${module_name}.${table_name}":`, e); + } + } +} + /** * Extracts the primary key from an object using a prioritized list of possible key names. * @param obj The object to extract the ID from. diff --git a/src/lib/ae_journals/db_journals.ts b/src/lib/ae_journals/db_journals.ts index cfbf6715..8acd350f 100644 --- a/src/lib/ae_journals/db_journals.ts +++ b/src/lib/ae_journals/db_journals.ts @@ -1,7 +1,10 @@ import Dexie, { type Table } from 'dexie'; +import { browser } from '$app/environment'; import type { key_val } from '$lib/stores/ae_stores'; import type { ae_Journal, ae_JournalEntry } from '$lib/types/ae_types'; +import { IDB_CONTENT_VERSIONS } from '$lib/stores/store_versions'; +import { check_and_clear_idb_tables } from '$lib/ae_core/core__idb_dexie'; // li = list // kv = key value list @@ -141,3 +144,14 @@ export class MySubClassedDexie extends Dexie { } export const db_journals = new MySubClassedDexie(); + +// On each page load, clear any tables whose content version has changed. +// Versions are defined in store_versions.ts IDB_CONTENT_VERSIONS.journals. +// Each table is only cleared once per version bump (tracked in localStorage). +if (browser) { + check_and_clear_idb_tables({ + db_instance: db_journals, + module_name: 'journals', + table_versions: IDB_CONTENT_VERSIONS.journals + }).catch((e) => console.warn('[db_journals] IDB version check failed:', e)); +} diff --git a/src/lib/stores/store_versions.ts b/src/lib/stores/store_versions.ts index 399825ec..c4aaefa1 100644 --- a/src/lib/stores/store_versions.ts +++ b/src/lib/stores/store_versions.ts @@ -37,6 +37,61 @@ export const AE_PRES_MGMT_LOC_VERSION = 1; // Added 2026-04-02: new standalone P export const AE_BADGES_LOC_VERSION = 1; // Added 2026-04-02: promoted from events_loc.badges export const AE_LEADS_LOC_VERSION = 1; // Added 2026-04-03: promoted from events_loc.leads +/** + * IDB_CONTENT_VERSIONS — per-table Dexie content version tracking. + * + * NOT YET ACTIVE. Wiring task tracked in TODO__Agents.md (post June 10). + * + * HOW IT WILL WORK (when wired): + * Each db_*.ts will call a helper (core__idb_dexie.ts) on open that checks a + * lightweight `_meta` table. If the stored version for a table doesn't match + * the constant here, the table is cleared and the version record is updated. + * The SWR pattern then repopulates from the API on next access. + * + * HOW TO USE (once active): + * Bump a table's version here when properties_to_save changes in a way that + * makes existing cached records stale (e.g. adding/removing stored fields, + * changing computed field behavior). Do NOT bump for schema-only changes + * (new indexes, new tables) — those belong in db.version() Dexie migrations. + * + * IDAA NOTES: + * IDAA tables (posts, archives) are already cleared aggressively via + * indexedDB.deleteDatabase() on sign-out and on auth failure in (idaa)/+layout.svelte. + * The content version check is a complementary mechanism for deploy-time resets, + * NOT a replacement for the auth-driven wipe. When wiring, ensure IDAA tables are + * only cleared on IDB open (not mid-session), and that the _meta table itself is + * also cleared when deleteDatabase() is called. + */ +export const IDB_CONTENT_VERSIONS = { + journals: { + journal: 3, + journal_entry: 3 // 2026-05-14: removed content_md_html + history_md_html from properties_to_save + }, + events: { + event: 1, + event_session: 1, + event_presenter: 1, + event_badge: 1, + event_device: 1, + event_location: 1, + event_file: 1 + }, + core: { + site_domain: 1, + person: 1, + user: 1 + }, + // IDAA modules — see IDAA NOTES above before wiring these + posts: { + post: 1, + post_comment: 1 + }, + archives: { + archive: 1, + archive_content: 1 + } +}; + // Version check side-effect: runs on import, before any persisted() call. // Guard presence of `localStorage` and its functions for safety (SSR, Node flags). if (