1import type { WriteQueueClient } from "@/Cloudflare/Queues/WriteQueue.ts";
2import * as Cause from "effect/Cause";
3import * as Effect from "effect/Effect";
4import type * as HttpServerRequest from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6
7/** Producer accepted the message(s). */
8const accepted = HttpServerResponse.json({ ok: true }, { status: 202 });
9const failed = (cause: Cause.Cause<unknown>) =>
10 HttpServerResponse.json({ error: Cause.pretty(cause) }, { status: 500 });
11
12/**
13 * Shared producer routes exercised by both the binding and HTTP fixtures so
14 * every method of {@link WriteQueueClient} is driven over `fetch`. The
15 * Cloudflare Queue binding is producer-only, so this is the full surface:
16 *
17 * - `POST /send` — `send(body)` with the default JSON content type.
18 * - `POST /send-text` — `send(body, { contentType: "text" })`.
19 * - `POST /sendBatch` — `sendBatch([...])` with JSON message bodies.
20 * - `POST /sendBatch-text` — `sendBatch([...])` with per-message `text`
21 * content type.
22 *
23 * (`raw` is the native-binding escape hatch and `Effect.die`s over the HTTP
24 * token, so it is intentionally not routed here.)
25 *
26 * Returns `undefined` when the path is not a producer route so the caller can
27 * fall through to a 404.
28 */
29export const producerRoutes = (
30 q: WriteQueueClient,
31 request: HttpServerRequest.HttpServerRequest,
32 url: URL,
33) =>
34 Effect.gen(function* () {
35 if (request.method !== "POST") return undefined;
36 switch (url.pathname) {
37 case "/send": {
38 const text = yield* request.text;
39 return yield* q.send({ text }).pipe(
40 Effect.matchCauseEffect({
41 onSuccess: () => accepted,
42 onFailure: failed,
43 }),
44 );
45 }
46 case "/send-text": {
47 const text = yield* request.text;
48 return yield* q.send(text, { contentType: "text" }).pipe(
49 Effect.matchCauseEffect({
50 onSuccess: () => accepted,
51 onFailure: failed,
52 }),
53 );
54 }
55 case "/sendBatch":
56 return yield* q
57 .sendBatch([{ body: { text: "a" } }, { body: { text: "b" } }])
58 .pipe(
59 Effect.matchCauseEffect({
60 onSuccess: () => accepted,
61 onFailure: failed,
62 }),
63 );
64 case "/sendBatch-text":
65 return yield* q
66 .sendBatch([
67 { body: "a", contentType: "text" },
68 { body: "b", contentType: "text" },
69 ])
70 .pipe(
71 Effect.matchCauseEffect({
72 onSuccess: () => accepted,
73 onFailure: failed,
74 }),
75 );
76 default:
77 return undefined;
78 }
79 });