From 7b8f4df8563f00f756d53167ea31ea033d21d089 Mon Sep 17 00:00:00 2001 From: AgentSeal Date: Tue, 28 Apr 2026 22:56:47 +0200 Subject: [PATCH] Fix review findings: error handling, constant dedup, migration persist - Wrap hydrateCache() in try/catch so disk errors don't crash commands that previously never touched the cache - Export MS_PER_DAY, BACKFILL_DAYS, toDateString from daily-cache.ts and remove duplicates from cli.ts - Remove double hydrateCache() call in report JSON path - Persist migrated cache to disk so old-version files aren't re-migrated on every run - Export emptyCache() for use as fallback on hydration failure --- src/cli.ts | 24 ++++++++++-------------- src/daily-cache.ts | 14 +++++++++----- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index c025d5e..116866c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,7 +7,7 @@ import { convertCost } from './currency.js' import { renderStatusBar } from './format.js' import { type PeriodData, type ProviderCost } from './menubar-json.js' import { buildMenubarPayload } from './menubar-json.js' -import { getDaysInRange, ensureCacheHydrated } from './daily-cache.js' +import { getDaysInRange, ensureCacheHydrated, emptyCache, MS_PER_DAY, BACKFILL_DAYS, toDateString } from './daily-cache.js' import { aggregateProjectsIntoDays, buildPeriodDataFromDays, dateKey } from './day-aggregator.js' import { CATEGORY_LABELS, type DateRange, type ProjectSummary, type TaskCategory } from './types.js' import { renderDashboard } from './dashboard.js' @@ -24,18 +24,15 @@ const require = createRequire(import.meta.url) const { version } = require('../package.json') import { loadCurrency, getCurrency, isValidCurrencyCode } from './currency.js' -const MS_PER_DAY = 24 * 60 * 60 * 1000 -const BACKFILL_DAYS = 365 - -function toDateString(date: Date): string { - return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` -} - -function hydrateCache() { - return ensureCacheHydrated( - (range) => parseAllSessions(range, 'all'), - aggregateProjectsIntoDays, - ) +async function hydrateCache() { + try { + return await ensureCacheHydrated( + (range) => parseAllSessions(range, 'all'), + aggregateProjectsIntoDays, + ) + } catch { + return emptyCache() + } } function getDateRange(period: string): { range: DateRange; label: string } { @@ -129,7 +126,6 @@ function toJsonPlanSummary(planUsage: PlanUsage): JsonPlanSummary { async function runJsonReport(period: Period, provider: string, project: string[], exclude: string[]): Promise { await loadPricing() - await hydrateCache() const { range, label } = getDateRange(period) const projects = filterProjectsByName(await parseAllSessions(range, provider), project, exclude) const report: ReturnType & { plan?: JsonPlanSummary } = buildJsonReport(projects, label, period) diff --git a/src/daily-cache.ts b/src/daily-cache.ts index f782346..6e30727 100644 --- a/src/daily-cache.ts +++ b/src/daily-cache.ts @@ -46,7 +46,7 @@ function getCachePath(): string { return join(getCacheDir(), DAILY_CACHE_FILENAME) } -function emptyCache(): DailyCache { +export function emptyCache(): DailyCache { return { version: DAILY_CACHE_VERSION, lastComputedDate: null, days: [] } } @@ -88,11 +88,15 @@ export async function loadDailyCache(): Promise { const raw = await readFile(path, 'utf-8') const parsed: unknown = JSON.parse(raw) if (isMigratableCache(parsed)) { - return { + const migrated: DailyCache = { version: DAILY_CACHE_VERSION, lastComputedDate: parsed.lastComputedDate, days: migrateDays(parsed.days), } + if (parsed.version < DAILY_CACHE_VERSION) { + await saveDailyCache(migrated).catch(() => {}) + } + return migrated } const oldVersion = (parsed as { version?: number })?.version if (typeof oldVersion === 'number') await backupOldCache(path, oldVersion) @@ -147,10 +151,10 @@ export function withDailyCacheLock(fn: () => Promise): Promise { return next } -const MS_PER_DAY = 24 * 60 * 60 * 1000 -const BACKFILL_DAYS = 365 +export const MS_PER_DAY = 24 * 60 * 60 * 1000 +export const BACKFILL_DAYS = 365 -function toDateString(date: Date): string { +export function toDateString(date: Date): string { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` }