|
|
@@ -2,8 +2,8 @@ import { type SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
|
|
|
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
|
|
|
import { type SQLiteTransaction } from "drizzle-orm/sqlite-core"
|
|
|
export * from "drizzle-orm"
|
|
|
+import { RuntimeFlags } from "@/effect/runtime-flags"
|
|
|
import { LocalContext } from "@/util/local-context"
|
|
|
-import { lazy } from "../util/lazy"
|
|
|
import { Global } from "@opencode-ai/core/global"
|
|
|
import * as Log from "@opencode-ai/core/util/log"
|
|
|
import { NamedError } from "@opencode-ai/core/util/error"
|
|
|
@@ -12,9 +12,8 @@ import { readFileSync, readdirSync, existsSync } from "fs"
|
|
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
|
import { InstallationChannel } from "@opencode-ai/core/installation/version"
|
|
|
import { InstanceState } from "@/effect/instance-state"
|
|
|
-import { iife } from "@/util/iife"
|
|
|
import { init } from "#db"
|
|
|
-import { Schema } from "effect"
|
|
|
+import { Effect, Schema } from "effect"
|
|
|
|
|
|
declare const OPENCODE_MIGRATIONS: { sql: string; timestamp: number; name: string }[] | undefined
|
|
|
|
|
|
@@ -24,24 +23,29 @@ export const NotFoundError = NamedError.create("NotFoundError", {
|
|
|
|
|
|
const log = Log.create({ service: "db" })
|
|
|
|
|
|
-export function getChannelPath() {
|
|
|
- if (["latest", "beta", "prod"].includes(InstallationChannel) || Flag.OPENCODE_DISABLE_CHANNEL_DB)
|
|
|
+type ChannelDbFlags = Pick<RuntimeFlags.Info, "disableChannelDb">
|
|
|
+
|
|
|
+const readRuntimeFlags = () =>
|
|
|
+ Effect.runSync(RuntimeFlags.Service.useSync((flags) => flags).pipe(Effect.provide(RuntimeFlags.defaultLayer)))
|
|
|
+
|
|
|
+export function getChannelPath(flags: ChannelDbFlags = readRuntimeFlags()) {
|
|
|
+ if (["latest", "beta", "prod"].includes(InstallationChannel) || flags.disableChannelDb)
|
|
|
return path.join(Global.Path.data, "opencode.db")
|
|
|
const safe = InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")
|
|
|
return path.join(Global.Path.data, `opencode-${safe}.db`)
|
|
|
}
|
|
|
|
|
|
-export const Path = iife(() => {
|
|
|
+export const getPath = (flags?: ChannelDbFlags) => {
|
|
|
if (Flag.OPENCODE_DB) {
|
|
|
if (Flag.OPENCODE_DB === ":memory:" || path.isAbsolute(Flag.OPENCODE_DB)) return Flag.OPENCODE_DB
|
|
|
return path.join(Global.Path.data, Flag.OPENCODE_DB)
|
|
|
}
|
|
|
- return getChannelPath()
|
|
|
-})
|
|
|
+ return getChannelPath(flags)
|
|
|
+}
|
|
|
|
|
|
export type Transaction = SQLiteTransaction<"sync", void>
|
|
|
|
|
|
-type Client = SQLiteBunDatabase
|
|
|
+type Client = ReturnType<typeof init>
|
|
|
|
|
|
type Journal = { sql: string; timestamp: number; name: string }[]
|
|
|
|
|
|
@@ -85,38 +89,55 @@ function migrations(dir: string): Journal {
|
|
|
return sql.sort((a, b) => a.timestamp - b.timestamp)
|
|
|
}
|
|
|
|
|
|
-export const Client = lazy(() => {
|
|
|
- log.info("opening database", { path: Path })
|
|
|
-
|
|
|
- const db = init(Path)
|
|
|
-
|
|
|
- db.run("PRAGMA journal_mode = WAL")
|
|
|
- db.run("PRAGMA synchronous = NORMAL")
|
|
|
- db.run("PRAGMA busy_timeout = 5000")
|
|
|
- db.run("PRAGMA cache_size = -64000")
|
|
|
- db.run("PRAGMA foreign_keys = ON")
|
|
|
- db.run("PRAGMA wal_checkpoint(PASSIVE)")
|
|
|
-
|
|
|
- // Apply schema migrations
|
|
|
- const entries =
|
|
|
- typeof OPENCODE_MIGRATIONS !== "undefined"
|
|
|
- ? OPENCODE_MIGRATIONS
|
|
|
- : migrations(path.join(import.meta.dirname, "../../migration"))
|
|
|
- if (entries.length > 0) {
|
|
|
- log.info("applying migrations", {
|
|
|
- count: entries.length,
|
|
|
- mode: typeof OPENCODE_MIGRATIONS !== "undefined" ? "bundled" : "dev",
|
|
|
- })
|
|
|
- if (Flag.OPENCODE_SKIP_MIGRATIONS) {
|
|
|
- for (const item of entries) {
|
|
|
- item.sql = "select 1;"
|
|
|
+let client: Client | undefined
|
|
|
+let loaded = false
|
|
|
+
|
|
|
+export const Client = Object.assign(
|
|
|
+ (flags?: ChannelDbFlags): Client => {
|
|
|
+ if (loaded) return client as Client
|
|
|
+
|
|
|
+ const dbPath = getPath(flags)
|
|
|
+ log.info("opening database", { path: dbPath })
|
|
|
+
|
|
|
+ const db = init(dbPath)
|
|
|
+
|
|
|
+ db.run("PRAGMA journal_mode = WAL")
|
|
|
+ db.run("PRAGMA synchronous = NORMAL")
|
|
|
+ db.run("PRAGMA busy_timeout = 5000")
|
|
|
+ db.run("PRAGMA cache_size = -64000")
|
|
|
+ db.run("PRAGMA foreign_keys = ON")
|
|
|
+ db.run("PRAGMA wal_checkpoint(PASSIVE)")
|
|
|
+
|
|
|
+ // Apply schema migrations
|
|
|
+ const entries =
|
|
|
+ typeof OPENCODE_MIGRATIONS !== "undefined"
|
|
|
+ ? OPENCODE_MIGRATIONS
|
|
|
+ : migrations(path.join(import.meta.dirname, "../../migration"))
|
|
|
+ if (entries.length > 0) {
|
|
|
+ log.info("applying migrations", {
|
|
|
+ count: entries.length,
|
|
|
+ mode: typeof OPENCODE_MIGRATIONS !== "undefined" ? "bundled" : "dev",
|
|
|
+ })
|
|
|
+ if (Flag.OPENCODE_SKIP_MIGRATIONS) {
|
|
|
+ for (const item of entries) {
|
|
|
+ item.sql = "select 1;"
|
|
|
+ }
|
|
|
}
|
|
|
+ applyMigrations(db, entries)
|
|
|
}
|
|
|
- applyMigrations(db, entries)
|
|
|
- }
|
|
|
|
|
|
- return db
|
|
|
-})
|
|
|
+ client = db
|
|
|
+ loaded = true
|
|
|
+ return db
|
|
|
+ },
|
|
|
+ {
|
|
|
+ reset: () => {
|
|
|
+ loaded = false
|
|
|
+ client = undefined
|
|
|
+ },
|
|
|
+ loaded: () => loaded,
|
|
|
+ },
|
|
|
+)
|
|
|
|
|
|
export function close() {
|
|
|
if (!Client.loaded()) return
|