import { BusEvent } from "@/bus/bus-event" import { Bus } from "@/bus" import { SessionID } from "./schema" import { Effect, Layer, Context } from "effect" import z from "zod" import { Database, eq, asc } from "../storage" import { TodoTable } from "./session.sql" export const Info = z .object({ content: z.string().describe("Brief description of the task"), status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"), priority: z.string().describe("Priority level of the task: high, medium, low"), }) .meta({ ref: "Todo" }) export type Info = z.infer export const Event = { Updated: BusEvent.define( "todo.updated", z.object({ sessionID: SessionID.zod, todos: z.array(Info), }), ), } export interface Interface { readonly update: (input: { sessionID: SessionID; todos: Info[] }) => Effect.Effect readonly get: (sessionID: SessionID) => Effect.Effect } export class Service extends Context.Service()("@opencode/SessionTodo") {} export const layer = Layer.effect( Service, Effect.gen(function* () { const bus = yield* Bus.Service const update = Effect.fn("Todo.update")(function* (input: { sessionID: SessionID; todos: Info[] }) { yield* Effect.sync(() => Database.transaction((db) => { db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run() if (input.todos.length === 0) return db.insert(TodoTable) .values( input.todos.map((todo, position) => ({ session_id: input.sessionID, content: todo.content, status: todo.status, priority: todo.priority, position, })), ) .run() }), ) yield* bus.publish(Event.Updated, input) }) const get = Effect.fn("Todo.get")(function* (sessionID: SessionID) { const rows = yield* Effect.sync(() => Database.use((db) => db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(), ), ) return rows.map((row) => ({ content: row.content, status: row.status, priority: row.priority, })) }) return Service.of({ update, get }) }), ) export const defaultLayer = layer.pipe(Layer.provide(Bus.layer)) export * as Todo from "./todo"