refactor(core): internalize effect sqlite adapter

This commit is contained in:
Dax Raad
2026-08-11 21:33:12 -04:00
parent 57b636df00
commit f6611a1f56
33 changed files with 8 additions and 182 deletions
-17
View File
@@ -375,7 +375,6 @@
"@modelcontextprotocol/sdk": "1.29.0",
"@opencode-ai/ai": "workspace:*",
"@opencode-ai/codemode": "workspace:*",
"@opencode-ai/effect-drizzle-sqlite": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/schema": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -477,20 +476,6 @@
"@parcel/watcher-win32-x64": "2.5.1",
},
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.18.15",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
},
"devDependencies": {
"@effect/sql-sqlite-bun": "catalog:",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:",
},
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.18.15",
@@ -2088,8 +2073,6 @@
"@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
"@opencode-ai/effect-drizzle-sqlite": ["@opencode-ai/effect-drizzle-sqlite@workspace:packages/effect-drizzle-sqlite"],
"@opencode-ai/enterprise": ["@opencode-ai/enterprise@workspace:packages/enterprise"],
"@opencode-ai/function": ["@opencode-ai/function@workspace:packages/function"],
-1
View File
@@ -100,7 +100,6 @@
"@ff-labs/fff-bun": "0.10.1",
"@ff-labs/fff-node": "0.10.1",
"@opencode-ai/codemode": "workspace:*",
"@opencode-ai/effect-drizzle-sqlite": "workspace:*",
"@opencode-ai/ai": "workspace:*",
"@opencode-ai/schema": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
+1 -1
View File
@@ -1,6 +1,6 @@
export * as Database from "./database.js"
import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
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"
+1
View File
@@ -0,0 +1 @@
export * from "./drizzle/index.js"
+1 -1
View File
@@ -3,7 +3,7 @@ export * as DatabaseMigration from "./migration.js"
import { sql } from "drizzle-orm"
import { Effect, Semaphore } from "effect"
import { supportsForeignKeyToggle } from "#sqlite"
import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import type { EffectDrizzleSqlite } from "./drizzle.js"
import { migrations } from "./migration.gen.js"
import schema from "./schema.gen.js"
import { Global } from "@opencode-ai/util/global"
+1 -1
View File
@@ -7,7 +7,7 @@ import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
import { AbsolutePath } from "../schema.js"
import { ProjectSchema } from "./schema.js"
import { ProjectDirectoryTable } from "./sql.js"
import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import type { EffectDrizzleSqlite } from "../database/drizzle.js"
import type { Project } from "../project.js"
export type Directory = Project.Directory
+1 -1
View File
@@ -1,4 +1,4 @@
import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import type { EffectDrizzleSqlite } from "../database/drizzle.js"
import { isNotNull, isNull, ne, or } from "drizzle-orm"
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"
import { absoluteArrayColumn, absoluteColumn } from "../database/path.js"
@@ -9,7 +9,7 @@ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
import { Effect } from "effect"
import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient"
import { isSqlError } from "effect/unstable/sql/SqlError"
import { EffectDrizzleSqlite } from "../src/index.js"
import { EffectDrizzleSqlite } from "@opencode-ai/core/database/drizzle"
const users = sqliteTable("users", {
id: integer().primaryKey({ autoIncrement: true }),
@@ -3,7 +3,7 @@ import { $ } from "bun"
import { fileURLToPath } from "url"
import path from "path"
import { SqliteClient } from "@effect/sql-sqlite-bun"
import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import { EffectDrizzleSqlite } from "@opencode-ai/core/database/drizzle"
import { Effect, Layer } from "effect"
import { sql } from "drizzle-orm"
import { DatabaseMigration } from "@opencode-ai/core/database/migration"
+1 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { SqliteClient } from "@effect/sql-sqlite-bun"
import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
import { EffectDrizzleSqlite } from "@opencode-ai/core/database/drizzle"
import { Database } from "@opencode-ai/core/database/database"
import { DatabaseMigration } from "@opencode-ai/core/database/migration"
import { V1Migration } from "@opencode-ai/core/database/v1-migration"
-19
View File
@@ -1,19 +0,0 @@
# Effect Drizzle SQLite
This package vendors a Drizzle Effect SQLite adapter for this repo.
- Keep this package generic: Drizzle + Effect + SQLite only.
- Do not add opencode-specific tables, paths, migrations, post-commit hooks, or domain storage APIs here.
- Runtime code should depend on generic `effect/unstable/sql/SqlClient`, not a specific SQLite driver.
- Concrete SQLite clients such as `@effect/sql-sqlite-bun` belong in tests or examples unless this package intentionally adds a driver-specific helper.
- Preserve Drizzle adapter naming and behavior where possible so this can be replaced by upstream `drizzle-orm/effect-sqlite` later.
- If touching copied Drizzle internals, compare with current `drizzle-orm@1.0.0-rc.2` declarations and runtime JS.
- If touching Effect APIs, verify against `/Users/kit/code/open-source/effect-smol`.
Useful entry points:
- `src/effect-sqlite/driver.ts`: creates the Effect-backed Drizzle database with `make` and `makeWithDefaults`.
- `src/effect-sqlite/session.ts`: adapts generic Effect `SqlClient` execution and transactions to Drizzle SQLite sessions.
- `src/sqlite-core/effect/*`: Effect-yieldable SQLite query builders.
- `src/internal/drizzle-utils.ts`: local typed shims for Drizzle runtime internals that RC2 does not expose in declarations.
- `examples/basic.ts`: minimal usage example with Bun SQLite.
@@ -1,92 +0,0 @@
import { SqliteClient } from "@effect/sql-sqlite-bun"
import { eq } from "drizzle-orm"
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Schema from "effect/Schema"
import { EffectDrizzleSqlite } from "../src/index.js"
const users = sqliteTable("users", {
id: integer().primaryKey({ autoIncrement: true }),
name: text().notNull(),
})
type User = typeof users.$inferSelect
const makeDatabase = EffectDrizzleSqlite.makeWithDefaults()
type DatabaseShape = Effect.Success<typeof makeDatabase>
const sqliteLayer = SqliteClient.layer({ filename: ":memory:", disableWAL: true })
class Database extends Context.Service<Database, DatabaseShape>()("@opencode/example/Database") {
static layer = Layer.effect(Database, makeDatabase).pipe(Layer.provide(sqliteLayer))
}
class UserStoreError extends Schema.TaggedErrorClass<UserStoreError>()("UserStoreError", {
message: Schema.String,
cause: Schema.optional(Schema.Defect()),
}) {}
const mapStoreError = (message: string) => (cause: unknown) => new UserStoreError({ message, cause })
interface UserStoreShape {
migrate(): Effect.Effect<void, UserStoreError>
create(name: string): Effect.Effect<void, UserStoreError>
rename(from: string, to: string): Effect.Effect<void, UserStoreError>
list(): Effect.Effect<User[], UserStoreError>
}
class UserStore extends Context.Service<UserStore, UserStoreShape>()("@opencode/example/UserStore") {
static layer = Layer.effect(
UserStore,
Effect.gen(function* () {
const db = yield* Database
return UserStore.of({
migrate: Effect.fn("UserStore.migrate")(function* () {
yield* EffectDrizzleSqlite.migrate(db, { migrationsFolder: `${import.meta.dirname}/migrations` }).pipe(
Effect.mapError((cause) => new UserStoreError({ message: "Failed to migrate users", cause })),
)
}),
create: Effect.fn("UserStore.create")(function* (name: string) {
yield* db
.insert(users)
.values({ name })
.pipe(Effect.asVoid, Effect.mapError(mapStoreError("Failed to create user")))
}),
rename: Effect.fn("UserStore.rename")(function* (from: string, to: string) {
yield* db
.transaction(
Effect.fnUntraced(function* (tx) {
yield* tx.insert(users).values({ name: from })
yield* tx.update(users).set({ name: to }).where(eq(users.name, from))
}),
{ behavior: "immediate" },
)
.pipe(Effect.asVoid, Effect.mapError(mapStoreError("Failed to rename user")))
}),
list: Effect.fn("UserStore.list")(function* () {
return yield* db
.select()
.from(users)
.pipe(Effect.mapError(mapStoreError("Failed to list users")))
}),
})
}),
).pipe(Layer.provide(Database.layer))
}
const program = Effect.gen(function* () {
const userStore = yield* UserStore
yield* userStore.migrate()
yield* userStore.create("Ada")
yield* userStore.rename("Grace", "Grace Hopper")
return yield* userStore.list()
})
const rows = await Effect.runPromise(program.pipe(Effect.provide(UserStore.layer)))
console.log(rows)
@@ -1,28 +0,0 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.15",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
"private": true,
"scripts": {
"test": "bun test --timeout 30000 --only-failures",
"typecheck": "tsgo --noEmit"
},
"exports": {
".": "./src/index.ts",
"./effect-sqlite": "./src/effect-sqlite/index.ts",
"./effect-sqlite/migrator": "./src/effect-sqlite/migrator.ts",
"./sqlite-core/effect": "./src/sqlite-core/effect/index.ts"
},
"devDependencies": {
"@effect/sql-sqlite-bun": "catalog:",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:"
},
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:"
}
}
@@ -1,18 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowImportingTsExtensions": false,
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"noUncheckedIndexedAccess": false,
"plugins": [
{
"name": "@effect/language-service",
"transform": "@effect/language-service/transform",
"namespaceImportPackages": ["effect", "@effect/*"]
}
]
}
}