import { Cause, Effect, Layer, Schema } from "effect"; import * as SqlClient from "effect/sql/SqlClient"; import type * as SqlConnection from "effect/sql/SqlConnection"; import * as Statement from "effect/sql/Statement"; import * as Reactivity from "effect/reactivity/Reactivity"; import { type Order, Store, StoreFailure } from "./contracts.ts"; import { Runtime } from "./host.effi.ts"; export interface Backend { readonly acquire: SqlConnection.Acquirer; } // Map each typed reason without discarding a simultaneous cleanup defect. const translate = ( effect: Effect.Effect, map: (error: E) => StoreFailure, ): Effect.Effect => Effect.catchCause(effect, (cause) => Effect.failCause(Cause.map(cause, map))); const integer = Schema.Finite.check( Schema.makeFilter((value) => Number.isSafeInteger(value) || "expected a safe integer" ), ); const receipts = Schema.NonEmptyArray( Schema.Struct({ sku: Schema.String, quantity: integer, total: integer }), ); export const StoreLive: Layer.Layer = Layer .effect(Store)(Effect.gen(function* () { const backend = yield* translate( Runtime.use((runtime) => runtime.database()), () => new StoreFailure({ kind: "Permanent", stage: "provider" }), ); const sql = yield* SqlClient.make({ acquirer: backend.acquire, compiler: Statement.makeCompilerSqlite(), spanAttributes: [], }).pipe(Effect.provide(Reactivity.layer)); return { save: (order: Order) => translate( sql.withTransaction(Effect.gen(function* () { // Classification happens here so a COMMIT error cannot masquerade as a write failure. const rows = yield* translate( sql`insert into orders (sku, quantity, total) values (${order.sku}, ${order.quantity}, ${order.total}) returning sku, quantity, total`, (error) => new StoreFailure({ kind: error.isRetryable ? "Transient" : "Permanent", stage: "write", }), ); const parsed = yield* translate( Schema.decodeUnknownEffect(receipts)(rows), () => new StoreFailure({ kind: "Permanent", stage: "row" }), ); return parsed[0]; })), (error) => error instanceof StoreFailure ? error : new StoreFailure({ kind: "Permanent", stage: error.reason.operation ?? "transaction", }), ), }; }));