1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import * as Option from "effect/Option";
4import * as Schema from "effect/Schema";
5import * as Stream from "effect/Stream";
6import * as FetchHttpClient from "effect/http/FetchHttpClient";
7import * as HttpServerRequest from "effect/http/HttpServerRequest";
8import * as HttpServerResponse from "effect/http/HttpServerResponse";
9import * as Rpc from "effect/rpc/Rpc";
10import * as RpcGroup from "effect/rpc/RpcGroup";
11import * as RpcSchema from "effect/rpc/RpcSchema";
12import * as RpcSerialization from "effect/rpc/RpcSerialization";
13import * as RpcServer from "effect/rpc/RpcServer";
14
15export const API = RpcGroup.make(
16 Rpc.make("Ping", {
17 payload: Schema.Void,
18 success: Schema.String,
19 }),
20 Rpc.make("Stream", {
21 payload: { upto: Schema.Number },
22 success: RpcSchema.Stream(Schema.Number, Schema.Never),
23 }),
24);
25
26const httpEffect = RpcServer.toHttpEffect(API).pipe(
27 Effect.provide(
28 Layer.mergeAll(
29 API.toLayer({
30 Ping: () => Effect.succeed("pong"),
31 Stream: ({ upto }) =>
32 Stream.fromIterable(Array.from({ length: upto }, (_, i) => i)),
33 }),
34 RpcSerialization.layerNdjson,
35 ),
36 ),
37);
38
39export default {
40 fetch(request: Request) {
41 return httpEffect.pipe(
42 Effect.flatMap((eff) => eff),
43 Effect.provide([
44 Layer.succeed(
45 HttpServerRequest.HttpServerRequest,
46 HttpServerRequest.fromWeb(request as any).modify({
47 remoteAddress: Option.fromUndefinedOr(
48 request.headers.get("cf-connecting-ip") ?? undefined,
49 ),
50 }),
51 ),
52 FetchHttpClient.layer,
53 ]),
54 Effect.flatMap((response) =>
55 Effect.context().pipe(
56 Effect.map((context) =>
57 HttpServerResponse.toWeb(response as any, {
58 context,
59 }),
60 ),
61 ),
62 ),
63 Effect.scoped,
64 Effect.runPromise,
65 );
66 },
67};