| 1 | import * 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. |
| 5 | import { Connection } from "@/Prisma/Connection.ts"; |
| 6 | import type { PostgresOrigin } from "@/Prisma/PostgresOrigin.ts"; |
| 7 | import { Postgres } from "@/Prisma/Postgres.ts"; |
| 8 | import { Project } from "@/Prisma/Project.ts"; |
| 9 | import * as SQL from "@/SQL/Postgres.ts"; |
| 10 | import * as Effect from "effect/Effect"; |
| 11 | import * as HttpServerRequest from "effect/http/HttpServerRequest"; |
| 12 | import * 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 | */ |
| 20 | export 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 | |
| 33 | export 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 | |
| 43 | const TABLE = "alchemy_prisma_widgets"; |
| 44 | |
| 45 | /** |
| 46 | * Effect-native Worker driving `SQL.Postgres` over a Hyperdrive connection |
| 47 | * to a Prisma Postgres origin. |
| 48 | */ |
| 49 | export 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 | ) {} |