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
This commit is contained in:
AgentSeal 2026-04-28 22:56:47 +02:00
parent 888f592bd2
commit 7b8f4df856
2 changed files with 19 additions and 19 deletions

View file

@ -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<void> {
await loadPricing()
await hydrateCache()
const { range, label } = getDateRange(period)
const projects = filterProjectsByName(await parseAllSessions(range, provider), project, exclude)
const report: ReturnType<typeof buildJsonReport> & { plan?: JsonPlanSummary } = buildJsonReport(projects, label, period)

View file

@ -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<DailyCache> {
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<T>(fn: () => Promise<T>): Promise<T> {
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')}`
}