import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path";import * as SqlClient from "effect/unstable/sql/SqlClient";import type { SqlError } from "effect/unstable/sql/SqlError"; import { runMigrations } from "../Migrations.ts";import { ServerConfig } from "../../config.ts"; type RuntimeSqliteLayerConfig = { readonly filename: string; readonly spanAttributes?: Record<string, unknown>;}; type Loader = { layer: (config: RuntimeSqliteLayerConfig) => Layer.Layer<SqlClient.SqlClient, SqlError>;};const defaultSqliteClientLoaders = { bun: () => import("@effect/sql-sqlite-bun/SqliteClient"), node: () => import("@t3tools/shared/nodeSqliteClient"),} satisfies Record<string, () => Promise<Loader>>; 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>(loader); return clientModule.layer(config);}, Layer.unwrap); const setup = Layer.effectDiscard( Effect.gen(function* () { const sql = yield* SqlClient.SqlClient; // CLI and server write from separate processes; wait rather than fail with SQLITE_BUSY. yield* sql`PRAGMA busy_timeout = 5000;`; yield* sql`PRAGMA foreign_keys = ON;`; yield* sql`PRAGMA journal_mode = WAL;`; 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.gen(function* () { const { dbPath } = yield* ServerConfig; return makeSqlitePersistenceLive(dbPath); }),);
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import type { SqlError } from "effect/unstable/sql/SqlError";
import { runMigrations } from "../Migrations.ts";
import { ServerConfig } from "../../config.ts";
type RuntimeSqliteLayerConfig = {
readonly filename: string;
readonly spanAttributes?: Record<string, unknown>;
};
type Loader = {
layer: (config: RuntimeSqliteLayerConfig) => Layer.Layer<SqlClient.SqlClient, SqlError>;
};
const defaultSqliteClientLoaders = {
bun: () => import("@effect/sql-sqlite-bun/SqliteClient"),
node: () => import("@t3tools/shared/nodeSqliteClient"),
} satisfies Record<string, () => Promise<Loader>>;
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>(loader);
return clientModule.layer(config);
}, Layer.unwrap);
const setup = Layer.effectDiscard(
Effect.gen(function* () {
const sql = yield* SqlClient.SqlClient;
// CLI and server write from separate processes; wait rather than fail with SQLITE_BUSY.
yield* sql`PRAGMA busy_timeout = 5000;`;
yield* sql`PRAGMA foreign_keys = ON;`;
yield* sql`PRAGMA journal_mode = WAL;`;
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.gen(function* () {
const { dbPath } = yield* ServerConfig;
return makeSqlitePersistenceLive(dbPath);
}),
);