mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-15 07:48:24 -04:00
15fd792d92
Review pass over the profile. fff.workerd.ts becomes the standard bind() shim over the shared fff module instead of a hand-copied 119-line type surface that had already drifted. The dead Options.paths and its redundant Global override are deleted. The copy-pasted MCP clientInfo block becomes a ServerOptions mcp.stdio capability flag handled by the standard routes replacement. Database.configuredClient joins configured() so the profile stops hand-assembling the node's Global dependency. ServerFetch.make folds overrides into BootOptions so the embed seam has one shape, and sdk-next's EmbedOptions aliases it. The drizzle session's duck-typed transactionStatements check becomes the named NativeTransactionSqlClient contract that sqlite.workerd satisfies. The bundled models.dev snapshot is decoded and normalized once per isolate instead of per runtime, which matters when one isolate hosts many Durable Objects. Spike-test interceptor and log-read boilerplate collapse into the existing helpers, and the vitest 3.2.7 pins get their rationale in the config.
78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
export * as Database from "./database.js"
|
|
|
|
import { EffectDrizzleSqlite } from "./drizzle.js"
|
|
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.js"
|
|
import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
|
|
|
|
const makeDatabase = EffectDrizzleSqlite.makeWithDefaults()
|
|
type DatabaseShape = Effect.Success<typeof makeDatabase>
|
|
|
|
export interface Interface {
|
|
db: DatabaseShape
|
|
}
|
|
|
|
export const Options = Schema.Struct({
|
|
path: Schema.optional(Schema.String),
|
|
})
|
|
export type Options = typeof Options.Type
|
|
|
|
export class Service extends Context.Service<Service, Interface>()("@opencode/storage/Database") {}
|
|
|
|
const databaseLayer = Layer.effect(
|
|
Service,
|
|
Effect.gen(function* () {
|
|
const db = yield* makeDatabase
|
|
|
|
if (supportsTuningPragmas) {
|
|
yield* db.run("PRAGMA journal_mode = WAL")
|
|
yield* db.run("PRAGMA synchronous = NORMAL")
|
|
yield* db.run("PRAGMA busy_timeout = 5000")
|
|
yield* db.run("PRAGMA cache_size = -64000")
|
|
yield* db.run("PRAGMA wal_checkpoint(PASSIVE)")
|
|
}
|
|
// Durable Object SQLite always enforces foreign keys and rejects the pragma.
|
|
if (supportsForeignKeyToggle) yield* db.run("PRAGMA foreign_keys = ON")
|
|
yield* DatabaseMigration.apply(db)
|
|
|
|
return { db }
|
|
}).pipe(Effect.orDie),
|
|
)
|
|
|
|
export function layer(options: Options = { path: ":memory:" }) {
|
|
return Layer.unwrap(
|
|
Effect.gen(function* () {
|
|
const provide = (filename: string) => layerFromClient.pipe(Layer.provide(sqliteLayer({ filename })))
|
|
const filename = options.path ?? ":memory:"
|
|
if (filename === ":memory:" || isAbsolute(filename)) return provide(filename)
|
|
const global = yield* Global.Service
|
|
return provide(join(global.data, filename))
|
|
}),
|
|
)
|
|
}
|
|
|
|
// The database service over an injected SqlClient, for runtimes that receive
|
|
// database storage instead of opening a filesystem path. Any client provided
|
|
// here still goes through the pragma guards and migrations; Global is required
|
|
// because migrations may read it (the v1 import).
|
|
export const layerFromClient: Layer.Layer<Service, never, SqlClient.SqlClient | Global.Service> = databaseLayer
|
|
|
|
export function configured(options?: Options) {
|
|
return makeGlobalNode({ service: Service, layer: layer(options), deps: [Global.node] })
|
|
}
|
|
|
|
/** `configured`, but over an injected SqlClient layer instead of a filesystem path. */
|
|
export function configuredClient(client: Layer.Layer<SqlClient.SqlClient>) {
|
|
return makeGlobalNode({
|
|
service: Service,
|
|
layer: layerFromClient.pipe(Layer.provide(client)),
|
|
deps: [Global.node],
|
|
})
|
|
}
|
|
|
|
export const node = configured({ path: ":memory:" })
|