From 532aebc36258060cbd4fd931d7f931807b1da849 Mon Sep 17 00:00:00 2001 From: Lin Wan Date: Sat, 16 May 2026 22:49:55 +0200 Subject: [PATCH] fix: invalidate cursor cache after missing-createdAt fix --- src/cursor-cache.ts | 6 +++-- tests/cursor-cache.test.ts | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/cursor-cache.test.ts diff --git a/src/cursor-cache.ts b/src/cursor-cache.ts index 390dcfa..0c0cccb 100644 --- a/src/cursor-cache.ts +++ b/src/cursor-cache.ts @@ -11,7 +11,9 @@ import type { ParsedProviderCall } from './providers/types.js' // router relies on those composer ids to bucket calls per project. // Version 2 caches contain `sessionId: 'unknown'` for every call and would // route everything to the orphan project, so we invalidate them. -const CURSOR_CACHE_VERSION = 3 +// Bumped to 4 after skipping bubble rows without createdAt: version 3 caches +// could contain historical Cursor bubbles timestamped with the parse time. +const CURSOR_CACHE_VERSION = 4 type ResultCache = { version?: number @@ -23,7 +25,7 @@ type ResultCache = { const CACHE_FILE = 'cursor-results.json' function getCacheDir(): string { - return join(homedir(), '.cache', 'codeburn') + return process.env['CODEBURN_CACHE_DIR'] ?? join(homedir(), '.cache', 'codeburn') } function getCachePath(): string { diff --git a/tests/cursor-cache.test.ts b/tests/cursor-cache.test.ts new file mode 100644 index 0000000..a79ae44 --- /dev/null +++ b/tests/cursor-cache.test.ts @@ -0,0 +1,46 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { mkdir, mkdtemp, rm, stat, writeFile } from 'fs/promises' +import { join } from 'path' +import { tmpdir } from 'os' + +import { readCachedResults } from '../src/cursor-cache.js' + +let tmpDir: string +let oldCacheDir: string | undefined + +beforeEach(async () => { + tmpDir = await mkdtemp(join(tmpdir(), 'cursor-cache-')) + oldCacheDir = process.env['CODEBURN_CACHE_DIR'] + process.env['CODEBURN_CACHE_DIR'] = join(tmpDir, 'cache') +}) + +afterEach(async () => { + if (oldCacheDir === undefined) { + delete process.env['CODEBURN_CACHE_DIR'] + } else { + process.env['CODEBURN_CACHE_DIR'] = oldCacheDir + } + await rm(tmpDir, { recursive: true, force: true }) +}) + +describe('cursor result cache', () => { + it('invalidates v3 caches written before the missing-createdAt fix', async () => { + const dbPath = join(tmpDir, 'state.vscdb') + await writeFile(dbPath, 'cursor db') + const fp = await stat(dbPath) + + const cacheDir = process.env['CODEBURN_CACHE_DIR']! + await mkdir(cacheDir, { recursive: true }) + await writeFile( + join(cacheDir, 'cursor-results.json'), + JSON.stringify({ + version: 3, + dbMtimeMs: fp.mtimeMs, + dbSizeBytes: fp.size, + calls: [], + }), + ) + + await expect(readCachedResults(dbPath)).resolves.toBeNull() + }) +})