fix: guard MetadataCache methods against use after close

Add early-return guards to getHeaders, getIndex, getManifest, and
updateCachedAt when the DB has been closed. This prevents "statement
has been finalized" errors when the process exit handler closes the
DB while async operations are still in flight.

Also change store controller close to flush (not close) the metadata
DB, since the exit handler handles cleanup.
This commit is contained in:
Zoltan Kochan
2026-04-01 16:55:07 +02:00
parent a042ebb8a2
commit cbf7dfcf65
2 changed files with 5 additions and 1 deletions

View File

@@ -184,6 +184,7 @@ export class MetadataCache {
* Cheap — no manifest data touched.
*/
getHeaders (name: string): MetadataHeaders | undefined {
if (this.closed) return undefined
const pending = this.findPendingIndex(name)
if (pending) {
return {
@@ -204,6 +205,7 @@ export class MetadataCache {
* Does NOT load per-version manifests — very cheap.
*/
getIndex (name: string): MetadataIndex | null {
if (this.closed) return null
const pending = this.findPendingIndex(name)
if (pending) {
return {
@@ -238,6 +240,7 @@ export class MetadataCache {
* Get a single version's manifest. Falls back from requested type to 'full'.
*/
getManifest (name: string, version: string, type: string): string | null {
if (this.closed) return null
const pending = this.findPendingManifest(name, version, type)
if (pending) return pending.manifest
const row = sqliteRetry(() => this.stmtGetManifest.get(name, version, type, type)) as { manifest: string } | undefined
@@ -297,6 +300,7 @@ export class MetadataCache {
* Update cachedAt without rewriting data.
*/
updateCachedAt (name: string, cachedAt: number): void {
if (this.closed) return
sqliteRetry(() => {
this.stmtUpdateCachedAt.run(cachedAt, name)
})

View File

@@ -141,7 +141,7 @@ export async function createNewStoreController (
const origClose = ctrl.close.bind(ctrl)
ctrl.close = async () => {
await origClose()
metadataDb.close()
metadataDb.flush()
}
return {
ctrl,