From c561849b48b4228a327942cc0543795f601433d7 Mon Sep 17 00:00:00 2001 From: huyplb Date: Tue, 18 Aug 2026 21:20:50 -0600 Subject: [PATCH] fix(history): a SQLite database is a path, not localhost//a/path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last of the schema-history friendliness pass, found by reading the shipped v0.2.111 build rather than the diff: the header and the database picker both rendered `sqlite · localhost//tmp/app.db · main`. The location was built by joining host and database with a slash, and a file dialect has no host — so a value that means nothing for SQLite or DuckDB was glued onto a path that already starts with one. `databaseLocation` now decides per dialect and both labels read it, instead of each joining the parts itself. The rest of that pass is confirmed live in v0.2.111: no "reused" and no "Lokee" in user-facing text, no MQT filter on a schema that has none, no React Flow badge, and the type filters match what the database actually contains. Co-Authored-By: Claude Opus 5 --- .../components/lokee-weave/LokeeWeaveView.tsx | 8 +++- .../src/frontend/lib/historyCompare.test.ts | 45 +++++++++++++++++++ apps/web/src/frontend/lib/historyCompare.ts | 21 ++++++++- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/apps/web/src/frontend/components/lokee-weave/LokeeWeaveView.tsx b/apps/web/src/frontend/components/lokee-weave/LokeeWeaveView.tsx index 00c0e7bf..5853852a 100644 --- a/apps/web/src/frontend/components/lokee-weave/LokeeWeaveView.tsx +++ b/apps/web/src/frontend/components/lokee-weave/LokeeWeaveView.tsx @@ -28,7 +28,11 @@ import { toast } from '../../store/toastStore'; import { useSyncStore } from '../../store/useSyncStore'; import { useUiStore } from '../../store/uiStore'; import { useLokeeHistoryStore } from '../../store/lokeeHistoryStore'; -import { resolveHistoryCompare, sortVersionsNewestFirst } from '../../lib/historyCompare'; +import { + databaseLocation, + resolveHistoryCompare, + sortVersionsNewestFirst, +} from '../../lib/historyCompare'; import { SQL_ICON_STROKE } from '../sql-editor/sqlIconStyle'; export interface LokeeWeaveViewProps { @@ -52,7 +56,7 @@ const EMPTY_DTO: VersionGraphDTO = { function describe(database: LokeeDatabase | undefined): string | undefined { if (!database) return undefined; - const where = [database.host, database.database].filter(Boolean).join('/'); + const where = databaseLocation(database); // ` · schema`, not `.schema`: a SQLite path already ends in `.db`, so the // dotted form rendered as `/tmp/app.db.main` and read like a file extension. const schema = database.schema ? ` · ${database.schema}` : ''; diff --git a/apps/web/src/frontend/lib/historyCompare.test.ts b/apps/web/src/frontend/lib/historyCompare.test.ts index 87d4d973..53acaecf 100644 --- a/apps/web/src/frontend/lib/historyCompare.test.ts +++ b/apps/web/src/frontend/lib/historyCompare.test.ts @@ -5,6 +5,7 @@ */ import { describe, expect, it } from 'vitest'; import { + databaseLocation, historyVersionLabel, lokeeDatabaseLabel, resolveHistoryCompare, @@ -141,3 +142,47 @@ describe('compare-only labelling', () => { ).toBe('v7 · before launch — compare only'); }); }); + +describe('databaseLocation', () => { + // `localhost//tmp/app.db` — a stray double slash in front of a path, from a + // host value that means nothing for a file dialect in the first place. + it.each(['sqlite', 'duckdb'])('drops the host for %s', (dialect) => { + expect(databaseLocation({ dialect, host: 'localhost', database: '/tmp/app.db' })).toBe( + '/tmp/app.db' + ); + }); + + it('keeps host and database for a server dialect', () => { + expect(databaseLocation({ dialect: 'postgres', host: 'localhost', database: 'foxdb' })).toBe( + 'localhost/foxdb' + ); + }); + + it('does not leave a dangling separator when one half is missing', () => { + expect(databaseLocation({ dialect: 'postgres', database: 'foxdb' })).toBe('foxdb'); + expect(databaseLocation({ dialect: 'postgres', host: 'db.internal' })).toBe('db.internal'); + }); + + it('is empty when there is nothing to show, so callers can fall back', () => { + expect(databaseLocation({ dialect: 'sqlite' })).toBe(''); + }); +}); + +describe('lokeeDatabaseLabel reads the same way', () => { + it('names a SQLite file without inventing a host', () => { + expect( + lokeeDatabaseLabel({ + id: 'abc12345', + dialect: 'sqlite', + host: 'localhost', + database: '/tmp/app.db', + schema: 'main', + versionCount: 3, + }) + ).toBe('SQLITE · /tmp/app.db · main (3 v)'); + }); + + it('falls back to a short id when the database has no location at all', () => { + expect(lokeeDatabaseLabel({ id: 'abcdef1234', dialect: 'sqlite' })).toContain('abcdef12'); + }); +}); diff --git a/apps/web/src/frontend/lib/historyCompare.ts b/apps/web/src/frontend/lib/historyCompare.ts index cb323990..b777bec5 100644 --- a/apps/web/src/frontend/lib/historyCompare.ts +++ b/apps/web/src/frontend/lib/historyCompare.ts @@ -93,6 +93,24 @@ export function historyVersionLabel( return base; } +/** + * Where a captured database lives, in the words that dialect uses. + * + * A file dialect has no host. Joining one on produced `localhost//tmp/app.db` — + * a stray double slash in front of a path, from a value that means nothing for + * SQLite or DuckDB in the first place. + */ +export function databaseLocation(database: { + dialect: string; + host?: string; + database?: string; +}): string { + const path = (database.database ?? '').trim(); + const isFile = database.dialect === 'sqlite' || database.dialect === 'duckdb'; + if (isFile) return path; + return [database.host, path].filter(Boolean).join('/'); +} + export function lokeeDatabaseLabel(database: { id: string; dialect: string; @@ -101,8 +119,7 @@ export function lokeeDatabaseLabel(database: { schema?: string; versionCount?: number; }): string { - const where = - [database.host, database.database].filter(Boolean).join('/') || database.id.slice(0, 8); + const where = databaseLocation(database) || database.id.slice(0, 8); const schema = database.schema ? ` · ${database.schema}` : ''; const versions = typeof database.versionCount === 'number' ? ` (${database.versionCount} v)` : '';