1import * as Alchemy from "alchemy";
2import * as Cloudflare from "alchemy/Cloudflare";
3import * as Drizzle from "alchemy/Drizzle";
4import * as Planetscale from "alchemy/Planetscale";
5import * as Effect from "effect/Effect";
6
7/**
8 * A Drizzle schema + PlanetScale MySQL database + feature branch. The
9 * branch's `migrations` prop is wired to the schema resource, so
10 * the provider order becomes:
11 *
12 * 1. `Drizzle.Schema` regenerates pending migration SQL files.
13 * 2. `Planetscale.MySQLBranch` scans the directory and applies new
14 * migrations transactionally.
15 */
16export const PlanetscaleDb = Effect.gen(function* () {
17 const { stage } = yield* Alchemy.Stack;
18
19 const schema = yield* Drizzle.Schema("app-schema", {
20 schema: "./src/schema.ts",
21 out: "./migrations",
22 dialect: "mysql",
23 });
24
25 // Stages are organised in two tiers:
26 //
27 // - `staging-*` stages own the long-lived PlanetScale database.
28 // - `pr-*` stages reference the parallel `staging-pr-*` database
29 // and only own ephemeral compute (branch + role + Hyperdrive + Worker).
30 //
31 // Deriving `staging-${stage}` instead of a single global "staging"
32 // keeps each test / PR isolated. Locally (`dev_<user>`, etc.) we create
33 // a fresh database.
34 const database = stage.startsWith("pr-")
35 ? yield* Planetscale.MySQLDatabase.ref("app-db", {
36 stage: `staging-${stage}`,
37 })
38 : yield* Planetscale.MySQLDatabase("app-db", {
39 region: { slug: "us-east" },
40 clusterSize: "PS_10",
41 });
42
43 const branch = yield* Planetscale.MySQLBranch("app-branch", {
44 database,
45 isProduction: false,
46 migrations: schema,
47 });
48
49 const password = yield* Planetscale.MySQLPassword("app-password", {
50 database,
51 branch,
52 role: "readwriter",
53 });
54
55 return { database, branch, password };
56});
57
58export const Hyperdrive: Effect.Effect<
59 Cloudflare.Hyperdrive.Connection,
60 never,
61 any
62> = Effect.gen(function* () {
63 const { password } = yield* PlanetscaleDb;
64 return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
65 origin: password.origin,
66 caching: { disabled: true },
67 });
68});