import { Context, Effect, Layer } from "effect" import { Database } from "./storage/db" import { DataMigrationTable } from "./data-migration.sql" import * as Log from "@opencode-ai/core/util/log" import { eq } from "drizzle-orm" export type Migration = { name: string run: Effect.Effect } const log = Log.create({ service: "data-migration" }) export interface Interface {} export class Service extends Context.Service()("@opencode/DataMigration") {} export const layer = Layer.effect( Service, Effect.gen(function* () { const migrations: Migration[] = [] yield* Effect.gen(function* () { if (migrations.length === 0) return // Migrations run in a background fiber, so they must be resumable until // their completion row is written. for (const migration of migrations) { const completed = Database.use((db) => db .select({ name: DataMigrationTable.name }) .from(DataMigrationTable) .where(eq(DataMigrationTable.name, migration.name)) .get(), ) if (completed) continue log.info("running data migration", { name: migration.name }) yield* migration.run.pipe(Effect.withSpan("DataMigration", { attributes: { name: migration.name } })) Database.use((db) => db .insert(DataMigrationTable) .values({ name: migration.name, time_completed: Date.now() }) .onConflictDoNothing() .run(), ) } }).pipe( Effect.tapCause((cause) => Effect.logError("failed to run data migrations", { cause })), Effect.ignore, Effect.forkScoped, ) return Service.of({}) }), ) export const defaultLayer = layer export * as DataMigration from "./data-migration"