1import * as Cloudflare from "@/Cloudflare/index.ts";
2// Deep imports keep the Worker bundle lean: the `@/Prisma` barrel pulls in
3// the local dev-database machinery (@prisma/dev -> pglite), which balloons
4// the script and has no business inside a deployed Worker.
5import { Connection } from "@/Prisma/Connection.ts";
6import type { PostgresOrigin } from "@/Prisma/PostgresOrigin.ts";
7import { Postgres } from "@/Prisma/Postgres.ts";
8import { Project } from "@/Prisma/Project.ts";
9import * as SQL from "@/SQL/Postgres.ts";
10import * as Effect from "effect/Effect";
11import * as HttpServerRequest from "effect/http/HttpServerRequest";
12import * as HttpServerResponse from "effect/http/HttpServerResponse";
13
14/**
15 * Prisma Postgres origin + Hyperdrive wiring shared by the fixture Worker.
16 * The Hyperdrive config fronts the direct (non-pooled) Prisma endpoint via
17 * `connection.origin`, the same shape Planetscale roles and Neon branches
18 * materialize.
19 */
20export const PrismaDb = Effect.gen(function* () {
21 const project = yield* Project("PrismaHyperdriveProject", {
22 createDatabase: false,
23 });
24 const database = yield* Postgres("PrismaHyperdriveDb", {
25 project,
26 });
27 const connection = yield* Connection("PrismaHyperdriveConnection", {
28 database,
29 });
30 return { project, database, connection };
31});
32
33export const Hyperdrive = Effect.gen(function* () {
34 const { connection } = yield* PrismaDb;
35 return yield* Cloudflare.Hyperdrive.Connection("PrismaHyperdriveEdge", {
36 origin: connection.origin.as<PostgresOrigin>(),
37 // The test asserts read-your-writes across separate HTTP requests;
38 // Hyperdrive's query cache (60s default) would serve stale SELECTs.
39 caching: { disabled: true },
40 });
41});
42
43const TABLE = "alchemy_prisma_widgets";
44
45/**
46 * Effect-native Worker driving `SQL.Postgres` over a Hyperdrive connection
47 * to a Prisma Postgres origin.
48 */
49export default class PrismaHyperdriveWorker extends Cloudflare.Worker<PrismaHyperdriveWorker>()(
50 "PrismaHyperdriveWorker",
51 {
52 main: import.meta.url,
53 },
54 Effect.gen(function* () {
55 const hd = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
56 const sql = yield* SQL.Postgres({ url: hd.connectionString });
57
58 return {
59 fetch: Effect.gen(function* () {
60 const request = yield* HttpServerRequest.HttpServerRequest;
61 const url = new URL(request.url, "http://x");
62
63 if (request.method === "POST" && url.pathname === "/widgets") {
64 const body = (yield* request.json) as { id: number; name: string };
65 yield* sql`CREATE TABLE IF NOT EXISTS ${sql(TABLE)} (id INT PRIMARY KEY, name TEXT NOT NULL)`;
66 yield* sql`INSERT INTO ${sql(TABLE)} (id, name) VALUES (${body.id}, ${body.name}) ON CONFLICT (id) DO UPDATE SET name = ${body.name}`;
67 return yield* HttpServerResponse.json({ ok: true });
68 }
69
70 if (request.method === "GET" && url.pathname === "/widgets") {
71 const widgets =
72 yield* sql`SELECT id, name FROM ${sql(TABLE)} ORDER BY id`;
73 return yield* HttpServerResponse.json({ widgets });
74 }
75
76 return HttpServerResponse.text("Not Found", { status: 404 });
77 }).pipe(
78 Effect.catchCause((cause) =>
79 HttpServerResponse.json({ error: String(cause) }, { status: 500 }),
80 ),
81 ),
82 };
83 }).pipe(Effect.provide(Cloudflare.Hyperdrive.ConnectBinding)),
84) {}