import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; // Cloudflare must see a rejected callback to roll back. Carry the local Effect // cause through that Promise boundary without flattening typed failures into // defects or committing a failed program's partial writes. export function transaction( storage: DurableObjectStorage, action: (tx: DurableObjectTransaction) => Effect.Effect, ) { class Rollback { constructor(readonly cause: Cause.Cause) {} } return Effect.tryPromise({ try: (signal) => storage.transaction(async (tx) => { const exit = await Effect.runPromiseExit(action(tx), { signal }); if (Exit.isSuccess(exit)) return exit.value; throw new Rollback(exit.cause); }), catch: (error) => error, }).pipe( Effect.catch((error) => error instanceof Rollback ? Effect.failCause(error.cause) : Effect.die(error), ), ); }