import type * as cf from "@cloudflare/workers-types"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as EffectHttp from "effect/unstable/http/HttpEffect"; import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; import * as Http from "../../Http.ts"; import { Request } from "./Request.ts"; import { isWorkerEvent, type WorkerServices } from "./Worker.ts"; export type HttpEffect = Http.HttpEffect; export const makeRequestHandler = ( handler: Http.HttpEffect | Effect.Effect>, ) => (event: any) => isWorkerEvent(event) && event.type === "fetch" ? makeRequestEffect(event.input, handler) : undefined; export const makeRequestEffect = ( webRequest: cf.Request, handler: Http.HttpEffect | Effect.Effect>, ) => { const safeHandler = Http.safeHttpEffect(handler); return Effect.gen(function* () { const request = HttpServerRequest.fromWeb( webRequest as any as globalThis.Request, ).modify({ remoteAddress: Option.fromUndefinedOr( webRequest.headers.get("cf-connecting-ip") ?? undefined, ), }); Object.defineProperty(request, "raw", { get: () => Object.assign(request.stream, { raw: webRequest.body, }), }); return yield* toHandledWebResponse(safeHandler).pipe( Effect.provide([ Layer.succeed(HttpServerRequest.HttpServerRequest, request), Layer.succeed(Request, webRequest as any), ]), ); }) as any; }; const toHandledWebResponse = ( handler: Effect.Effect, ) => Effect.gen(function* () { // `toHandled` exposes the final response through this callback, not its // return value. Keep the assignment isolated here so callers get Response. const context = yield* Effect.context(); const webResponse = yield* Deferred.make(); yield* EffectHttp.toHandled(handler, (request, response) => Deferred.succeed( webResponse, // Conversion to web response with options matches `EffectHttp.toWebHandler`'s callback. HttpServerResponse.toWeb(EffectHttp.scopeTransferToStream(response), { withoutBody: request.method === "HEAD", context, }), ), ); return yield* Deferred.await(webResponse); }); export { isScopeEjected } from "../../Http.ts";