feat(idb): add IDB content version check system, wire to journals
Adds check_and_clear_idb_tables() helper in core__idb_dexie.ts that clears
Dexie tables when their content version changes. Version numbers live in
IDB_CONTENT_VERSIONS (store_versions.ts); state tracked in localStorage
(ae_idb_ver__{module}__{table}) so each table clears exactly once per bump.
Wired into db_journals.ts (pilot): journal_entry at v3 clears stale
content_md_html/history_md_html data cached before the properties_to_save fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
### [Stores] Refactor — Phase 2c (deferred)
|
||||||
Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining:
|
Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,53 @@
|
|||||||
import type { Dexie, Table } from 'dexie';
|
import type { Dexie, Table } from 'dexie';
|
||||||
import { browser } from '$app/environment';
|
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<string, number>;
|
||||||
|
log_lvl?: number;
|
||||||
|
}): Promise<void> {
|
||||||
|
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.
|
* Extracts the primary key from an object using a prioritized list of possible key names.
|
||||||
* @param obj The object to extract the ID from.
|
* @param obj The object to extract the ID from.
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import Dexie, { type Table } from 'dexie';
|
import Dexie, { type Table } from 'dexie';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
import type { key_val } from '$lib/stores/ae_stores';
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
import type { ae_Journal, ae_JournalEntry } from '$lib/types/ae_types';
|
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
|
// li = list
|
||||||
// kv = key value list
|
// kv = key value list
|
||||||
@@ -141,3 +144,14 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const db_journals = new MySubClassedDexie();
|
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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -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_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
|
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.
|
// Version check side-effect: runs on import, before any persisted() call.
|
||||||
// Guard presence of `localStorage` and its functions for safety (SSR, Node flags).
|
// Guard presence of `localStorage` and its functions for safety (SSR, Node flags).
|
||||||
if (
|
if (
|
||||||
|
|||||||
Reference in New Issue
Block a user