Skip to content

Commit ae335a6

Browse files
committed
feat: initial support for sqlite
1 parent 4be1bcc commit ae335a6

21 files changed

Lines changed: 2159 additions & 1576 deletions

File tree

apps/deditor/package.json

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,68 +33,69 @@
3333
"@deditor-app/deditor-icons": "workspace:^",
3434
"@deditor-app/shared-schemas": "workspace:^",
3535
"@duckdb/node-api": "1.3.0-alpha.21",
36-
"@electric-sql/pglite": "^0.3.3",
36+
"@electric-sql/pglite": "^0.3.7",
3737
"@electron-toolkit/preload": "^3.0.2",
3838
"@electron-toolkit/utils": "^4.0.0",
3939
"@guiiai/logg": "^1.0.10",
40-
"@huggingface/transformers": "^3.5.2",
40+
"@huggingface/transformers": "^3.7.1",
41+
"@libsql/client": "^0.15.11",
4142
"@moeru/std": "0.1.0-beta.1",
4243
"@proj-airi/drizzle-duckdb-wasm": "^0.4.28",
4344
"@proj-airi/ui": "^0.6.1",
4445
"@stdlib/string": "^0.3.3",
4546
"@tanstack/vue-table": "^8.21.3",
46-
"@tanstack/vue-virtual": "^3.13.11",
47-
"@vueuse/core": "^13.4.0",
47+
"@tanstack/vue-virtual": "^3.13.12",
48+
"@vueuse/core": "^13.6.0",
4849
"@vueuse/motion": "^3.0.3",
49-
"@vueuse/shared": "^13.4.0",
50-
"@xsai-transformers/embed": "^0.0.6",
51-
"@xsai-transformers/shared": "^0.0.6",
50+
"@vueuse/shared": "^13.6.0",
51+
"@xsai-transformers/embed": "^0.0.7",
52+
"@xsai-transformers/shared": "^0.0.7",
5253
"@xsai/embed": "catalog:",
5354
"@xsai/shared": "catalog:",
54-
"mysql2": "^3.14.1",
55+
"mysql2": "^3.14.3",
5556
"nanoid": "^5.1.5",
5657
"ofetch": "^1.4.1",
5758
"pathe": "^2.0.3",
5859
"pinia": "^3.0.3",
5960
"splitpanes": "^4.0.4",
6061
"std-env": "^3.9.0",
61-
"vue": "^3.5.17",
62+
"vue": "^3.5.18",
6263
"vue-router": "^4.5.1"
6364
},
6465
"devDependencies": {
6566
"@deditor-app/drizzle-orm-icons": "workspace:^",
6667
"@deditor-app/shared": "workspace:^",
6768
"@deditor-app/umap-wasm": "workspace:^",
6869
"@iconify-json/ph": "^1.2.2",
69-
"@iconify-json/simple-icons": "^1.2.40",
70-
"@iconify-json/svg-spinners": "^1.2.2",
70+
"@iconify-json/simple-icons": "^1.2.47",
71+
"@iconify-json/svg-spinners": "^1.2.4",
7172
"@radix-icons/vue": "^1.0.0",
7273
"@tresjs/cientos": "^4.3.1",
7374
"@tresjs/core": "^4.3.6",
7475
"@types/debug": "^4.1.12",
7576
"@types/splitpanes": "^2.2.6",
76-
"@types/three": "^0.177.0",
77-
"@unocss/reset": "^66.2.3",
78-
"@vitejs/plugin-vue": "^5.2.4",
77+
"@types/three": "^0.179.0",
78+
"@unocss/reset": "^66.4.2",
79+
"@vitejs/plugin-vue": "^6.0.1",
7980
"class-variance-authority": "^0.7.1",
8081
"clsx": "^2.1.1",
8182
"debug": "^4.4.1",
82-
"drizzle-orm": "^0.44.2",
83-
"electron": "^36.5.0",
83+
"drizzle-orm": "^0.44.4",
84+
"electron": "^36.7.4",
8485
"electron-builder": "^26.0.12",
85-
"electron-vite": "^3.1.0",
86+
"electron-vite": "^4.0.0",
8687
"ml-pca": "^4.1.1",
8788
"msvana-tsne": "^0.1.2",
8889
"postgres": "^3.4.7",
89-
"reka-ui": "^2.3.1",
90+
"reka-ui": "^2.4.1",
9091
"tailwind-merge": "^3.3.1",
91-
"three": "^0.177.0",
92-
"tw-animate-css": "^1.3.4",
92+
"three": "^0.179.1",
93+
"tw-animate-css": "^1.3.6",
9394
"unplugin-vue-macros": "^2.14.5",
94-
"unplugin-vue-markdown": "^28.3.1",
95-
"unplugin-vue-router": "^0.12.0",
96-
"vite-plugin-vue-devtools": "^7.7.7",
95+
"unplugin-vue-markdown": "^29.1.0",
96+
"unplugin-vue-router": "^0.15.0",
97+
"vite-plugin-vue-devtools": "^8.0.0",
9798
"vite-plugin-vue-layouts": "^0.11.0",
98-
"vue-tsc": "^2.2.10"
99+
"vue-tsc": "^2.2.12"
99100
}
100101
}

apps/deditor/src/main/ipc/databases/local/pglite-fs.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import type { BrowserWindow } from 'electron'
44

55
import { nanoid } from '@deditor-app/shared'
66
import * as schema from '@deditor-app/shared-schemas'
7-
import { postgresInformationSchemaColumns, postgresPgCatalogPgAm, postgresPgCatalogPgAttribute, postgresPgCatalogPgClass, postgresPgCatalogPgIndex, postgresPgCatalogPgNamespace, postgresPgCatalogPgType } from '@deditor-app/shared-schemas'
7+
import {
8+
postgresInformationSchemaColumns,
9+
postgresPgCatalogPgAm,
10+
postgresPgCatalogPgAttribute,
11+
postgresPgCatalogPgClass,
12+
postgresPgCatalogPgIndex,
13+
postgresPgCatalogPgNamespace,
14+
postgresPgCatalogPgType,
15+
} from '@deditor-app/shared-schemas'
816
import { PGlite } from '@electric-sql/pglite'
917
import { bloom } from '@electric-sql/pglite/contrib/bloom'
1018
import { citext } from '@electric-sql/pglite/contrib/citext'
@@ -83,11 +91,9 @@ export function registerPGLiteDatabaseDialect(window: BrowserWindow) {
8391

8492
await pgDrizzle.execute('SELECT 1')
8593
await pgDrizzle.execute(sql`CREATE SCHEMA IF NOT EXISTS "public"`)
86-
await pgDrizzle.execute(sql`SET search_path TO "public"`)
87-
await pgDrizzle.execute(sql`CREATE TABLE IF NOT EXISTS public."test_table" (
88-
id SERIAL PRIMARY KEY,
89-
name TEXT NOT NULL DEFAULT ''
90-
)`)
94+
if (!parsedDSN.searchParams.get('searchPath')) {
95+
await pgDrizzle.execute(sql`SET search_path TO "public"`)
96+
}
9197

9298
return { databaseSessionId: dbSessionId, dialect: 'pglite' }
9399
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { SQLiteMethods } from '@deditor-app/shared'
2+
import type { Client } from '@libsql/client'
3+
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
4+
import type { BrowserWindow } from 'electron'
5+
6+
import { nanoid } from '@deditor-app/shared'
7+
import * as schema from '@deditor-app/shared-schemas'
8+
import { useLogg } from '@guiiai/logg'
9+
import { createClient } from '@libsql/client'
10+
import { drizzle } from 'drizzle-orm/libsql'
11+
12+
import { defineIPCHandler } from '../../define-ipc-handler'
13+
14+
const databaseSessions = new Map<string, { drizzle: LibSQLDatabase<typeof schema>, client: Client }>()
15+
16+
export function registerSQLiteDatabaseDialect(window: BrowserWindow) {
17+
const log = useLogg('sqlite-database-dialect').useGlobalConfig()
18+
19+
defineIPCHandler<SQLiteMethods>(window, 'databaseLocalSQLite', 'connect')
20+
.handle(async (_, { dsn }) => {
21+
try {
22+
const parsedDSN = new URL(dsn)
23+
24+
const sqliteClient = createClient({
25+
url: `file://${parsedDSN.searchParams.get('dbFilePath') || parsedDSN.pathname}`,
26+
})
27+
28+
const sqliteDrizzle = drizzle(sqliteClient, { schema })
29+
const dbSessionId = nanoid()
30+
databaseSessions.set(dbSessionId, { drizzle: sqliteDrizzle, client: sqliteClient })
31+
32+
return { databaseSessionId: dbSessionId, dialect: 'sqlite' }
33+
}
34+
catch (err) {
35+
log.withError(err).error('failed to connect to local SQLite database')
36+
throw err
37+
}
38+
})
39+
40+
defineIPCHandler<SQLiteMethods>(window, 'databaseLocalSQLite', 'query')
41+
.handle(async (_, { databaseSessionId, statement, parameters }) => {
42+
if (!databaseSessions.has(databaseSessionId)) {
43+
throw new Error('Database session ID not found in session map, please connect to the database first.')
44+
}
45+
46+
try {
47+
const dbSession = databaseSessions.get(databaseSessionId)!
48+
const res = await dbSession.client.execute(statement, parameters)
49+
return { databaseSessionId, results: res.rows }
50+
}
51+
catch (err) {
52+
log.withError(err).withFields({ databaseSessionId, statement }).error('failed to query local SQLite database')
53+
throw err
54+
}
55+
})
56+
57+
defineIPCHandler<SQLiteMethods>(window, 'databaseLocalSQLite', 'listTables')
58+
.handle(async (_, { databaseSessionId }) => {
59+
if (!databaseSessions.has(databaseSessionId)) {
60+
throw new Error('Database session ID not found in session map, please connect to the database first.')
61+
}
62+
63+
try {
64+
const dbSession = databaseSessions.get(databaseSessionId)!
65+
const res = await dbSession.drizzle.query.sqliteSqliteSchema.findMany()
66+
return { databaseSessionId, results: res }
67+
}
68+
catch (err) {
69+
log.withError(err).withFields({ databaseSessionId }).error('failed to query local SQLite database to list tables')
70+
throw err
71+
}
72+
})
73+
}

apps/deditor/src/main/ipc/databases/remote/postgres.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export function registerPostgresJsDatabaseDialect(window: BrowserWindow) {
6565
databaseSessions.set(dbSessionId, { drizzle: pgDrizzle, client: pgClient })
6666

6767
await pgDrizzle.execute('SELECT 1')
68+
6869
return { databaseSessionId: dbSessionId, dialect: 'postgres' }
6970
}
7071
catch (err) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { SQLiteMethods } from '@deditor-app/shared'
2+
3+
import { ref } from 'vue'
4+
5+
import { defineClientMethod } from '../../define-client-method'
6+
7+
export function useLocalSQLite() {
8+
const methods = <TMethod extends keyof SQLiteMethods>(method: TMethod) => defineClientMethod<SQLiteMethods, TMethod>('databaseLocalSQLite', method)
9+
const databaseSessionId = ref<string>()
10+
11+
return {
12+
connect: async (dsn: string) => {
13+
const id = await methods('connect').call({ dsn })
14+
databaseSessionId.value = id.databaseSessionId
15+
16+
return id
17+
},
18+
execute: async <R = Record<string, unknown>>(statement: string, parameters: any[] = []): Promise<R[]> => {
19+
if (!databaseSessionId.value) {
20+
throw new Error('Database session ID is not set. Please connect to a database first.')
21+
}
22+
23+
const res = await methods('query').call({
24+
databaseSessionId: databaseSessionId.value!,
25+
statement,
26+
parameters,
27+
})
28+
29+
return res.results as R[]
30+
},
31+
listTables: async () => {
32+
if (!databaseSessionId.value) {
33+
throw new Error('Database session ID is not set. Please connect to a database first.')
34+
}
35+
36+
const res = await methods('listTables').call({ databaseSessionId: databaseSessionId.value! })
37+
return res.results
38+
},
39+
}
40+
}

0 commit comments

Comments
 (0)