1import * as Effect from "effect/Effect";
2import * as Redacted from "effect/Redacted";
3import type * as Scope from "effect/Scope";
4import type { Pool, PoolOptions } from "mysql2/promise";
5
6/**
7 * Lazily load the `mysql2/promise` driver (an optional peer dependency of
8 * alchemy) with a descriptive failure when it isn't installed. The
9 * promise flavor is the canonical surface — its `.pool` property exposes
10 * the callback pool for consumers (like Kysely) that drive that API.
11 */
12export const importMySql = (): Promise<typeof import("mysql2/promise")> =>
13 import("mysql2/promise")
14 .then((mod) =>
15 (mod as { default?: { createPool?: unknown } }).default?.createPool !==
16 undefined
17 ? (mod as unknown as { default: typeof import("mysql2/promise") })
18 .default
19 : mod,
20 )
21 .catch((cause) => {
22 throw new Error(
23 "Failed to load the 'mysql2' driver. Install the optional peer dependency 'mysql2' to connect to MySQL.",
24 { cause },
25 );
26 });
27
28/**
29 * Open a raw `mysql2/promise` pool on the current `Scope` — `pool.end()`
30 * runs when the scope closes. Pair with `makeExecutionMemo` for the
31 * one-pool-per-event shape workerd and Lambda require.
32 *
33 * `connectionLimit` defaults to 1: per-execution pools never need more
34 * than one connection.
35 */
36export const openMySQLPool = (
37 url: Effect.Effect<Redacted.Redacted<string>>,
38 config?: Omit<PoolOptions, "uri">,
39): Effect.Effect<Pool, never, Scope.Scope> =>
40 Effect.gen(function* () {
41 const mysql = yield* Effect.promise(importMySql);
42 const uri = Redacted.value(yield* url);
43 return yield* Effect.acquireRelease(
44 Effect.sync(() =>
45 mysql.createPool({ uri, connectionLimit: 1, ...config }),
46 ),
47 (pool) => Effect.promise(() => pool.end()),
48 );
49 });