feat(core): database layer over injected sqlite

This commit is contained in:
Kit Langton
2026-08-10 23:27:26 -04:00
parent 0f56ebdb28
commit 3bbc3fc267
2 changed files with 25 additions and 4 deletions
+9 -3
View File
@@ -3,6 +3,7 @@ export * as Database from "./database"
import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import { sqliteLayer, supportsForeignKeyToggle, supportsTuningPragmas } from "#sqlite"
import { Context, Effect, Layer, Schema } from "effect"
import type { SqlClient } from "effect/unstable/sql"
import { Global } from "@opencode-ai/util/global"
import { isAbsolute, join } from "path"
import { DatabaseMigration } from "./migration"
@@ -46,14 +47,19 @@ export function layer(options: Options = { path: ":memory:" }) {
return Layer.unwrap(
Effect.gen(function* () {
const global = yield* Global.Service
const provide = (filename: string) => databaseLayer.pipe(Layer.provide(sqliteLayer({ filename })))
const filename = options.path ?? ":memory:"
if (filename === ":memory:" || isAbsolute(filename)) return provide(filename)
return provide(join(global.data, filename))
if (filename === ":memory:" || isAbsolute(filename)) return layerWith(sqliteLayer({ filename }))
return layerWith(sqliteLayer({ filename: join(global.data, filename) }))
}),
)
}
// Builds the database service over an already-configured SqlClient layer for
// runtimes that receive database storage instead of opening a filesystem path.
export function layerWith(sqlite: Layer.Layer<SqlClient.SqlClient>) {
return databaseLayer.pipe(Layer.provide(sqlite))
}
export function configured(options?: Options) {
return makeGlobalNode({ service: Service, layer: layer(options), deps: [Global.node] })
}
+16 -1
View File
@@ -1,10 +1,11 @@
import { describe, expect, test } from "bun:test"
import { Database } from "bun:sqlite"
import { Effect } from "effect"
import { Effect, Layer } from "effect"
import { SqlClient } from "effect/unstable/sql"
import { SqlError } from "effect/unstable/sql/SqlError"
import { sqliteLayer } from "@opencode-ai/core/database/sqlite.workerd"
import type { DurableObjectStorage } from "@opencode-ai/core/database/sqlite.workerd"
import { tempGlobalLayer } from "./fixture/global"
// Emulates the Durable Object storage API over bun:sqlite so the adapter can
// be verified without workerd or Cloudflare runtime dependencies.
@@ -119,4 +120,18 @@ describe("sqlite.workerd", () => {
)
expect(error).toBeInstanceOf(SqlError)
})
test("boots the full database layer with migrations over injected storage", async () => {
const storage = makeFakeStorage()
const core = await import("@opencode-ai/core/database/database")
await Effect.runPromise(
Effect.scoped(Layer.build(core.Database.layerWith(sqliteLayer({ storage })).pipe(Layer.provide(tempGlobalLayer)))),
)
const names = storage.sql
.exec("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
.toArray()
.map((row) => row.name)
expect(names).toContain("migration")
expect(names).toContain("session_v2")
})
})