Skip to content

Commit 1a7371a

Browse files
authored
feat(db): drop built-in database adapters in favor of db0 connectors (#3010)
1 parent c11f90a commit 1a7371a

17 files changed

+154
-374
lines changed

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"c12": "^2.0.1",
5858
"chokidar": "^4.0.3",
5959
"consola": "^3.4.0",
60+
"db0": "^0.2.1",
6061
"defu": "^6.1.4",
6162
"destr": "^2.0.3",
6263
"fast-glob": "^3.3.3",
@@ -84,7 +85,6 @@
8485
"slugify": "^1.6.6",
8586
"socket.io-client": "^4.8.1",
8687
"tar": "^7.4.3",
87-
"typescript": "5.7.3",
8888
"ufo": "^1.5.4",
8989
"unified": "^11.0.5",
9090
"unist-util-stringify-position": "^4.0.0",
@@ -96,7 +96,7 @@
9696
},
9797
"peerDependencies": {
9898
"@libsql/client": "*",
99-
"pg": "*"
99+
"@electric-sql/pglite": "*"
100100
},
101101
"devDependencies": {
102102
"@cloudflare/workers-types": "^4.20250121.0",
@@ -121,6 +121,7 @@
121121
"micromark-util-types": "^2.0.1",
122122
"nuxt": "^3.15.2",
123123
"release-it": "^18.1.1",
124+
"typescript": "5.7.3",
124125
"vitest": "^3.0.3",
125126
"vue-tsc": "^2.2.0",
126127
"wrangler": "^3.103.2"

pnpm-lock.yaml

+57-143
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/module.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { findPreset } from './presets'
3030
import type { Manifest } from './types/manifest'
3131
import { setupPreview } from './utils/preview/module'
3232
import { parseSourceBase } from './utils/source'
33-
import { getLocalDatabase } from './utils/sqlite'
33+
import { getLocalDatabase, resolveDatabaseAdapter } from './utils/sqlite'
3434

3535
// Export public utils
3636
export * from './utils'
@@ -165,9 +165,9 @@ export default defineNuxtModule<ModuleOptions>({
165165
const preset = findPreset(nuxt)
166166
await preset.setupNitro(config, { manifest, resolver })
167167

168-
const adapter = config.runtimeConfig!.content!.database?.type || options.database.type || 'sqlite'
169168
config.alias ||= {}
170-
config.alias['#content/adapter'] = resolver.resolve(`./runtime/adapters/${adapter}`)
169+
config.alias['#content/adapter'] = resolveDatabaseAdapter(config.runtimeConfig!.content!.database?.type || options.database.type, resolver)
170+
config.alias['#content/local-adapter'] = resolveDatabaseAdapter(options._localDatabase!.type || 'sqlite', resolver)
171171

172172
config.handlers ||= []
173173
config.handlers.push({

src/runtime/adapters/d1.ts

-24
This file was deleted.

src/runtime/adapters/libsql.ts

-26
This file was deleted.

src/runtime/adapters/postgres.ts

-43
This file was deleted.

src/runtime/adapters/sqlite.ts

-11
This file was deleted.

src/runtime/internal/bunsqlite.ts

+1-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import * as zlib from 'node:zlib'
2-
import { isAbsolute } from 'pathe'
3-
import type { Database as BunDatabaseType } from 'bun:sqlite'
42

53
/**
64
* CompressionStream and DecompressionStream polyfill for Bun
@@ -42,31 +40,4 @@ if (!globalThis.CompressionStream) {
4240
} as unknown as typeof DecompressionStream
4341
}
4442

45-
function getBunDatabaseSync() {
46-
// A top level import will make Nuxt complain about a missing module
47-
// eslint-disable-next-line @typescript-eslint/no-require-imports
48-
return require('bun:sqlite').Database
49-
}
50-
51-
let db: BunDatabaseType
52-
export const getBunSqliteDatabaseAdapter = (opts: { filename: string }) => {
53-
const Database = getBunDatabaseSync()
54-
if (!db) {
55-
const filename = !opts || isAbsolute(opts?.filename || '') || opts?.filename === ':memory:'
56-
? opts?.filename
57-
: new URL(opts.filename, (globalThis as unknown as { _importMeta_: { url: string } })._importMeta_.url).pathname
58-
db = new Database(process.platform === 'win32' && filename.startsWith('/') ? filename.slice(1) : filename, { create: true })
59-
}
60-
61-
return {
62-
async all<T>(sql: string, params?: Array<number | string | boolean>): Promise<T[]> {
63-
return params ? db.prepare(sql).all(...params) as T[] : db.prepare(sql).all() as T[]
64-
},
65-
async first<T>(sql: string, params?: Array<number | string | boolean>): Promise<T | null> {
66-
return params ? db.prepare(sql).get(...params) as T : db.prepare(sql).get() as T
67-
},
68-
async exec(sql: string): Promise<unknown> {
69-
return db.prepare(sql).run()
70-
},
71-
}
72-
}
43+
export { default as default } from 'db0/connectors/bun-sqlite'

src/runtime/internal/database-adapter.ts

-31
This file was deleted.

src/runtime/internal/database.client.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ import { fetchDatabase } from './api'
66
import { checksums, tables } from '#content/manifest'
77

88
let db: Database
9-
109
export function loadDatabaseAdapter<T>(collection: T): DatabaseAdapter {
1110
return {
12-
all: async <T>(sql: string, params: DatabaseBindParams) => {
11+
all: async <T>(sql: string, params?: DatabaseBindParams) => {
1312
await loadAdapter(collection)
1413

1514
return db
1615
.exec({ sql, bind: params, rowMode: 'object', returnValue: 'resultRows' })
1716
.map(row => refineContentFields(sql, row) as T)
1817
},
19-
first: async <T>(sql: string, params: DatabaseBindParams) => {
18+
first: async <T>(sql: string, params?: DatabaseBindParams) => {
2019
await loadAdapter(collection)
2120

2221
return refineContentFields(

src/runtime/internal/database.server.ts

+35-11
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,50 @@
1-
import type { SqliteDatabaseConfig, DatabaseAdapter, RuntimeConfig } from '@nuxt/content'
1+
import type { DatabaseAdapter, RuntimeConfig } from '@nuxt/content'
22
import type { H3Event } from 'h3'
3+
import { isAbsolute } from 'pathe'
4+
import type { Connector } from 'db0'
35
import { decompressSQLDump } from './dump'
46
import { fetchDatabase } from './api'
7+
import { refineContentFields } from './collection'
58
import { tables, checksums } from '#content/manifest'
69
import adapter from '#content/adapter'
10+
import localAdapter from '#content/local-adapter'
711

812
export default function loadDatabaseAdapter(config: RuntimeConfig['content']) {
913
const { database, localDatabase } = config
1014

11-
let _adapter: DatabaseAdapter
15+
let _adapter: Connector
1216
async function loadAdapter() {
1317
if (!_adapter) {
1418
if (import.meta.dev || ['nitro-prerender', 'nitro-dev'].includes(import.meta.preset as string)) {
15-
_adapter = await loadSqliteAdapter(localDatabase)
19+
_adapter = await localAdapter(refineDatabaseConfig(localDatabase))
1620
}
1721
else {
18-
_adapter = adapter(database)
22+
_adapter = adapter(refineDatabaseConfig(database as unknown))
1923
}
2024
}
2125

2226
return _adapter
2327
}
24-
2528
return <DatabaseAdapter>{
26-
all: async (sql, params) => {
29+
all: async (sql, params = []) => {
2730
const db = await loadAdapter()
28-
return await db.all<Record<string, unknown>>(sql, params)
31+
const result = await db.prepare(sql).all(...params)
32+
33+
if (!result) {
34+
return []
35+
}
36+
37+
return result.map((item: unknown) => refineContentFields(sql, item))
2938
},
30-
first: async (sql, params) => {
39+
first: async (sql, params = []) => {
3140
const db = await loadAdapter()
32-
return await db.first<Record<string, unknown>>(sql, params)
41+
const item = await db.prepare(sql).get(...params)
42+
43+
if (!item) {
44+
return item
45+
}
46+
47+
return refineContentFields(sql, item)
3348
},
3449
exec: async (sql) => {
3550
const db = await loadAdapter()
@@ -93,6 +108,15 @@ async function loadDatabaseDump(event: H3Event, collection: string): Promise<str
93108
})
94109
}
95110

96-
function loadSqliteAdapter(config: SqliteDatabaseConfig) {
97-
return import('../adapters/sqlite').then(m => m.default(config))
111+
function refineDatabaseConfig(config: { filename?: string }) {
112+
config = { ...config }
113+
if ('filename' in config) {
114+
const filename = isAbsolute(config?.filename || '') || config?.filename === ':memory:'
115+
? config?.filename
116+
: new URL(config.filename, (globalThis as unknown as { _importMeta_: { url: string } })._importMeta_.url).pathname
117+
118+
config.filename = process.platform === 'win32' && filename.startsWith('/') ? filename.slice(1) : filename
119+
}
120+
121+
return config
98122
}

src/runtime/internal/sqlite.ts

-24
This file was deleted.

src/types/database.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export type DatabaseBindParams = Array<DatabaseBindable>
1+
import type { Primitive, Connector } from 'db0'
2+
3+
export type CacheEntry = { id: string, checksum: string, parsedContent: string }
4+
5+
export type DatabaseBindParams = Primitive[]
26

37
export type DatabaseBindable = number | string | boolean | null | undefined
48

@@ -11,12 +15,12 @@ export interface DatabaseAdapter {
1115
export type DatabaseAdapterFactory<Options> = (otps?: Options) => DatabaseAdapter
1216

1317
export interface LocalDevelopmentDatabase {
14-
fetchDevelopmentCache(): Promise<Record<string, { checksum: string, parsedContent: string }>>
15-
fetchDevelopmentCacheForKey(key: string): Promise<{ id: string, checksum: string, parsedContent: string } | undefined>
18+
fetchDevelopmentCache(): Promise<Record<string, CacheEntry>>
19+
fetchDevelopmentCacheForKey(key: string): Promise<CacheEntry | undefined>
1620
insertDevelopmentCache(id: string, checksum: string, parsedContent: string): void
1721
deleteDevelopmentCache(id: string): void
1822
dropContentTables(): void
1923
exec(sql: string): void
2024
close(): void
21-
database?: DatabaseAdapter
25+
database?: Connector
2226
}

src/types/global.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ declare module '#content/adapter' {
2727
export { adapter as default }
2828
}
2929

30+
declare module '#content/local-adapter' {
31+
import type { DatabaseAdapter } from './database'
32+
33+
const adapter: DatabaseAdapter
34+
35+
export { adapter as default }
36+
}
37+
3038
declare module '#content/collections' {
3139
export const collections: Record<string, unknown>
3240
}

0 commit comments

Comments
 (0)