refactor(effect-drizzle-sqlite): simplify single-row reads

This commit is contained in:
Kit Langton 2026-05-02 23:13:28 -04:00
parent 25e546c837
commit fd4887d45d
4 changed files with 34 additions and 14 deletions

View file

@ -63,6 +63,10 @@ type SelectLike<A = unknown> = EffectLikeQuery<A> & {
readonly all: () => A
}
type GetLike<A = unknown> = EffectLikeQuery & {
readonly get: () => A
}
type MutationLike<A = unknown> = EffectLikeQuery<A> & {
readonly all: () => A
readonly run: () => A
@ -104,6 +108,8 @@ const fromMutation = (query: MutationLike) => fromSync(query, () => (query.confi
const fromCount = (query: CountLike) => fromSync(query, () => Number(query.session.values(query.sql)[0]?.[0] ?? 0))
export const getOne = <A>(query: GetLike<A>) => fromSync(query, () => query.get())
const fromExecuteResult = (result: unknown) => {
if (result && typeof result === "object" && "sync" in result && typeof result.sync === "function") {
return result.sync()
@ -155,6 +161,7 @@ const attachTransaction = <
TRelations extends AnyRelations = EmptyRelations,
>(db: SQLiteBunDatabase<TSchema, TRelations> & { readonly $client: Database }): EffectSQLiteDatabase<TSchema, TRelations> => {
const txStack: Array<SQLiteTransaction<"sync", void, TSchema, TRelations>> = []
const bound = new WeakMap<object, Map<PropertyKey, unknown>>()
const current = () => txStack.at(-1) ?? db
const runTransaction = (target: SQLiteBunDatabase<TSchema, TRelations> | SQLiteTransaction<"sync", void, TSchema, TRelations>) =>
target.transaction.bind(target) as (
@ -195,7 +202,11 @@ const attachTransaction = <
const target = current()
const value = Reflect.get(target, property)
return typeof value === "function" ? value.bind(target) : value
if (typeof value !== "function") return value
const methods = bound.get(target) ?? new Map<PropertyKey, unknown>()
bound.set(target, methods)
if (!methods.has(property)) methods.set(property, value.bind(target))
return methods.get(property)
},
}) as EffectSQLiteDatabase<TSchema, TRelations>
}

View file

@ -5,7 +5,7 @@ import { relations } from "drizzle-orm/_relations"
import { drizzle as drizzleBun } from "drizzle-orm/bun-sqlite"
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
import { Cause, Effect, Exit } from "effect"
import { EffectDrizzleQueryError, make, type EffectSQLiteDatabase } from "../src"
import { EffectDrizzleQueryError, getOne, make, type EffectSQLiteDatabase } from "../src"
const users = sqliteTable("users", {
id: integer().primaryKey(),
@ -176,6 +176,15 @@ describe("effect drizzle sqlite", () => {
}),
)
testEffect("supports single-row select effects", () =>
Effect.gen(function* () {
yield* db.insert(users).values({ id: 1, name: "Ada" })
expect(yield* getOne(db.select().from(users).where(eq(users.id, 1)))).toEqual({ id: 1, name: "Ada" })
expect(yield* getOne(db.select().from(users).where(eq(users.id, 2)))).toBeUndefined()
}),
)
testEffect("nested pipeable transactions commit or roll back with the outer transaction", () =>
Effect.gen(function* () {
yield* Effect.gen(function* () {

View file

@ -6,6 +6,7 @@ import { ProjectID } from "@/project/schema"
import { MessageID, SessionID } from "@/session/schema"
import { PermissionTable } from "@/session/session.sql"
import { DatabaseEffect } from "@/storage/db-effect"
import { getOne } from "@opencode-ai/effect-drizzle-sqlite"
import { eq } from "drizzle-orm"
import { zod } from "@/util/effect-zod"
import * as Log from "@opencode-ai/core/util/log"
@ -156,14 +157,15 @@ export const layer = Layer.effect(
const db = yield* DatabaseEffect.Service
const state = yield* InstanceState.make<State>(
Effect.fn("Permission.state")(function* (ctx) {
const rows = yield* db
.select()
.from(PermissionTable)
.where(eq(PermissionTable.project_id, ctx.project.id))
.pipe(Effect.orDie)
const row = yield* getOne(
db
.select()
.from(PermissionTable)
.where(eq(PermissionTable.project_id, ctx.project.id)),
).pipe(Effect.orDie)
const state = {
pending: new Map<PermissionID, PendingEntry>(),
approved: rows[0]?.data ?? [],
approved: row?.data ?? [],
}
yield* Effect.addFinalizer(() =>

View file

@ -10,6 +10,7 @@ import { Session } from "@/session/session"
import { MessageV2 } from "@/session/message-v2"
import type { SessionID } from "@/session/schema"
import { DatabaseEffect } from "@/storage/db-effect"
import { getOne } from "@opencode-ai/effect-drizzle-sqlite"
import { eq } from "drizzle-orm"
import { Config } from "@/config/config"
import * as Log from "@opencode-ai/core/util/log"
@ -224,12 +225,9 @@ export const layer = Layer.effect(
})
const get = Effect.fnUntraced(function* (sessionID: SessionID) {
const rows = yield* db
.select()
.from(SessionShareTable)
.where(eq(SessionShareTable.session_id, sessionID))
.pipe(Effect.orDie)
const row = rows[0]
const row = yield* getOne(
db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)),
).pipe(Effect.orDie)
if (!row) return
return { id: row.id, secret: row.secret, url: row.url } satisfies Share
})