| 1 | import * as Effect from "effect/Effect"; |
| 2 | import * as Redacted from "effect/Redacted"; |
| 3 | import type * as Scope from "effect/Scope"; |
| 4 | import type { Pool, PoolConfig } from "pg"; |
| 5 | |
| 6 | /** |
| 7 | * Lazily load the raw `pg` driver (an optional peer dependency of |
| 8 | * alchemy) with a descriptive failure when it isn't installed. CJS/ESM |
| 9 | * interop is normalized — callers always get the module's named surface. |
| 10 | */ |
| 11 | export const importPg = (): Promise<typeof import("pg")> => |
| 12 | import("pg") |
| 13 | .then((mod) => |
| 14 | (mod as { default?: { Pool?: unknown } }).default?.Pool !== undefined |
| 15 | ? (mod as unknown as { default: typeof import("pg") }).default |
| 16 | : mod, |
| 17 | ) |
| 18 | .catch((cause) => { |
| 19 | throw new Error( |
| 20 | "Failed to load the 'pg' driver. Install the optional peer dependency 'pg' to connect to Postgres.", |
| 21 | { cause }, |
| 22 | ); |
| 23 | }); |
| 24 | |
| 25 | /** |
| 26 | * Open a raw `pg.Pool` on the current `Scope` — `pool.end()` runs when the |
| 27 | * scope closes. Pair with `makeExecutionMemo` for the one-pool-per-event |
| 28 | * shape workerd and Lambda require (see `SQL/Postgres.ts` for the |
| 29 | * effect-sql equivalent). |
| 30 | * |
| 31 | * `max` defaults to 1: per-execution pools never need more than one |
| 32 | * connection. |
| 33 | */ |
| 34 | export const openPostgresPool = ( |
| 35 | url: Effect.Effect<Redacted.Redacted<string>>, |
| 36 | config?: Omit<PoolConfig, "connectionString">, |
| 37 | ): Effect.Effect<Pool, never, Scope.Scope> => |
| 38 | Effect.gen(function* () { |
| 39 | const pg = yield* Effect.promise(importPg); |
| 40 | const connectionString = Redacted.value(yield* url); |
| 41 | return yield* Effect.acquireRelease( |
| 42 | Effect.sync(() => new pg.Pool({ connectionString, max: 1, ...config })), |
| 43 | (pool) => Effect.promise(() => pool.end()), |
| 44 | ); |
| 45 | }); |