feat: precache resilience — circuit breaker, forensic diag log, safe version check

- Circuit breaker: abort preload after 5 consecutive network failures
  (success or 404 resets streak) instead of grinding doomed retries
- localStorage diag ring buffer (survives cache wipes): preload-start/
  done/abort, manifest-unavailable, version events; shown in Settings
- checkDataVersion: no longer wipes IDB on transient read errors
  (DataCache.has tri-state probe); logs old value on real migrations
- loadManifest: null result no longer cached forever (per-session fallback lock-in)
This commit is contained in:
catarrh committed 2026-08-22 22:39:37 +03:00
1 parent ee5fb17c6b
commit 8c4f60d635
6 files changed
+140 -7

No files matched your search

+15 -2
View File
@@ -11,6 +11,7 @@ import { JDate } from '../core/jdate';
import { loadSettings, type Settings } from './settings-view';
import { getTranslations, type LanguageCode } from '../core/i18n';
import { DataCache } from '../core/data-cache';
import { diagLog } from '../core/diag-log';
type View = 'calendar' | 'bible' | 'prayer' | 'akathists' | 'parimii' | 'horologion' | 'sbornik' | 'paraclete' | 'irmologion' | 'menaion' | 'triodion' | 'lives' | 'settings';
@@ -75,13 +76,25 @@ export class App {
private async checkDataVersion() {
const DATA_VERSION = '2026-08-04'; // bump when data structure changes
try {
const present = await DataCache.has('__data_version__');
if (present === null) {
// Unreadable IDB — never wipe the cache on a flaky read
diagLog('version-check', { result: 'read-error', action: 'skip' });
return;
}
if (!present) {
await DataCache.set('__data_version__', DATA_VERSION);
diagLog('version-init');
return;
}
const cachedVer = await DataCache.get('__data_version__') as string | null;
if (cachedVer !== DATA_VERSION) {
await DataCache.clear();
await DataCache.set('__data_version__', DATA_VERSION);
diagLog('version-clear', { old: cachedVer });
}
} catch {
// Silently fail — cache is best-effort
} catch (err) {
diagLog('idb-error', { where: 'checkDataVersion', msg: String(err) });
}
}