codeburn/app/vite.config.ts
iamtoruk e9655fa03c fix: hydration completeness markers, cold-only splash, honest switching, prefetch
P0: an interrupted cold hydration left partial caches that the
emptiness check read as warm — the daily backfill froze (missing older
days) while session parsing healed gradually (drifting totals),
poisoning every surface sharing the cache. Both caches now carry an
explicit complete marker: partial resumes cold under the hydration
lock; unmarked caches self-heal with one re-hydration. Regression test
seeds the exact frozen artifact and asserts bit-for-bit convergence
with a never-interrupted run.

- splash indexing reveals only on genuinely cold scans (explicit cold
  flag in the progress protocol), never on warm incremental re-parses
- uncached filter switches clear to skeleton instead of showing the
  previous filter's numbers; cached switches paint same-commit;
  background prefetch warms every detected provider after idle
- build stamp (git sha + date) in splash and About ends build ambiguity
- payload parity test: identical payloads on identical cache, and
  payload totals equal the CLI report path
- Sessions empty state keeps the provider filter row; zero savings
  line hidden; TCC usage-description strings + Full Disk Access docs
- warm profile documented: optimize scan ~1.47s dominant (safe
  refactor deferred), parse ~1s, aggregation ~30ms

Root 1848, app 320, typechecks clean.
2026-07-16 14:05:11 -07:00

47 lines
1.8 KiB
TypeScript

import { execSync } from 'node:child_process'
import { defineConfig, type Plugin } from 'vite'
import react from '@vitejs/plugin-react'
// Build stamp baked in at package/build time so About + the splash footer can
// name the exact commit running. Best-effort: a non-git checkout still builds.
function buildStamp(): { sha: string; date: string } {
let sha = 'unknown'
try {
sha = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim() || 'unknown'
// A build off a dirty tree (local fix build) must never read as the clean
// release of the same commit — that ambiguity is exactly what a build stamp
// exists to kill.
if (execSync('git status --porcelain', { encoding: 'utf8' }).trim()) sha += '-dirty'
} catch { /* not a git checkout */ }
return { sha, date: new Date().toISOString().slice(0, 10) }
}
const stamp = buildStamp()
// The production index.html ships a strict `script-src 'self'`. In dev, Vite's
// React Fast Refresh preamble is injected as an inline <script>, which that CSP
// blocks; relax script-src to allow inline scripts for the dev server only.
function devCsp(): Plugin {
return {
name: 'codeburn-dev-csp',
apply: 'serve',
transformIndexHtml(html) {
return html.replace("script-src 'self'", "script-src 'self' 'unsafe-inline'")
},
}
}
// Renderer-only Vite config. The Electron main/preload are compiled separately
// by tsconfig.electron.json. `base: './'` so the built index.html loads its
// assets over file:// in production.
export default defineConfig({
root: 'renderer',
base: './',
plugins: [react(), devCsp()],
define: {
__BUILD_SHA__: JSON.stringify(stamp.sha),
__BUILD_DATE__: JSON.stringify(stamp.date),
},
server: { host: '127.0.0.1', port: 5173, strictPort: true },
build: { outDir: '../dist/renderer', emptyOutDir: true },
})