import { Effect, Layer, FileSystem, Path } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import { runMigrations } from "../Migrations.ts"; import { ServerConfig } from "../../config.ts"; type RuntimeSqliteLayerConfig = { readonly filename: string; readonly spanAttributes?: Record; }; type Loader = { layer: (config: RuntimeSqliteLayerConfig) => Layer.Layer; }; const defaultSqliteClientLoaders = { bun: () => import("@effect/sql-sqlite-bun/SqliteClient"), node: () => import("../NodeSqliteClient.ts"), } satisfies Record Promise>; const makeRuntimeSqliteLayer = Effect.fn("makeRuntimeSqliteLayer")(function* ( config: RuntimeSqliteLayerConfig, ) { const runtime = process.versions.bun !== undefined ? "bun" : "node"; const loader = defaultSqliteClientLoaders[runtime]; const clientModule = yield* Effect.promise(loader); return clientModule.layer(config); }, Layer.unwrap); const setup = Layer.effectDiscard( Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; yield* sql`PRAGMA journal_mode = WAL;`; yield* sql`PRAGMA foreign_keys = ON;`; yield* runMigrations(); }), ); export const makeSqlitePersistenceLive = Effect.fn("makeSqlitePersistenceLive")(function* ( dbPath: string, ) { const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; yield* fs.makeDirectory(path.dirname(dbPath), { recursive: true }); return Layer.provideMerge( setup, makeRuntimeSqliteLayer({ filename: dbPath, spanAttributes: { "db.name": path.basename(dbPath), "service.name": "t3-server", }, }), ); }, Layer.unwrap); export const SqlitePersistenceMemory = Layer.provideMerge( setup, makeRuntimeSqliteLayer({ filename: ":memory:" }), ); export const layerConfig = Layer.unwrap( Effect.map(Effect.service(ServerConfig), ({ dbPath }) => makeSqlitePersistenceLive(dbPath)), );