diff --git a/.changeset/nervous-comics-fetch.md b/.changeset/nervous-comics-fetch.md new file mode 100644 index 0000000000..62063cf70a --- /dev/null +++ b/.changeset/nervous-comics-fetch.md @@ -0,0 +1,7 @@ +--- +"@effect/platform": patch +--- + +ensure /platform HttpApp.toWebHandler runs Stream's with the current runtime + +Also add runtime options to HttpServerResponse.toWeb diff --git a/packages/platform/src/HttpApp.ts b/packages/platform/src/HttpApp.ts index 920b354769..8ef9df6e68 100644 --- a/packages/platform/src/HttpApp.ts +++ b/packages/platform/src/HttpApp.ts @@ -138,12 +138,12 @@ export const withPreResponseHandler = dual< */ export const toWebHandlerRuntime = (runtime: Runtime.Runtime) => { const run = Runtime.runFork(runtime) - return (self: Default, middleware: HttpMiddleware | undefined) => + return (self: Default, middleware?: HttpMiddleware | undefined) => (request: Request): Promise => new Promise((resolve) => { const fiber = run(Effect.provideService( toHandled(self, (request, response) => { - resolve(ServerResponse.toWeb(response, request.method === "HEAD")) + resolve(ServerResponse.toWeb(response, { withoutBody: request.method === "HEAD", runtime })) return Effect.void }, middleware), ServerRequest.HttpServerRequest, diff --git a/packages/platform/src/HttpServerResponse.ts b/packages/platform/src/HttpServerResponse.ts index 480e637139..f0a624d150 100644 --- a/packages/platform/src/HttpServerResponse.ts +++ b/packages/platform/src/HttpServerResponse.ts @@ -5,6 +5,7 @@ import type { ParseOptions } from "@effect/schema/AST" import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" import type { Inspectable } from "effect/Inspectable" +import type * as Runtime from "effect/Runtime" import type * as Stream from "effect/Stream" import type { Cookie, Cookies, CookiesError } from "./Cookies.js" import type * as PlatformError from "./Error.js" @@ -353,4 +354,10 @@ export const setStatus: { * @since 1.0.0 * @category conversions */ -export const toWeb: (response: HttpServerResponse, withoutBody?: boolean | undefined) => Response = internal.toWeb +export const toWeb: ( + response: HttpServerResponse, + options?: { + readonly withoutBody?: boolean | undefined + readonly runtime?: Runtime.Runtime | undefined + } +) => Response = internal.toWeb diff --git a/packages/platform/src/internal/httpServerResponse.ts b/packages/platform/src/internal/httpServerResponse.ts index 3971da7313..ef1ceff541 100644 --- a/packages/platform/src/internal/httpServerResponse.ts +++ b/packages/platform/src/internal/httpServerResponse.ts @@ -4,6 +4,7 @@ import * as Effect from "effect/Effect" import * as Effectable from "effect/Effectable" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" +import * as Runtime from "effect/Runtime" import * as Stream from "effect/Stream" import * as Cookies from "../Cookies.js" import type * as PlatformError from "../Error.js" @@ -504,7 +505,10 @@ export const setBody = dual< }) /** @internal */ -export const toWeb = (response: ServerResponse.HttpServerResponse, withoutBody = false): Response => { +export const toWeb = (response: ServerResponse.HttpServerResponse, options?: { + readonly withoutBody?: boolean | undefined + readonly runtime?: Runtime.Runtime | undefined +}): Response => { const headers = new globalThis.Headers(response.headers) if (!Cookies.isEmpty(response.cookies)) { const toAdd = Cookies.toSetCookieHeaders(response.cookies) @@ -512,7 +516,7 @@ export const toWeb = (response: ServerResponse.HttpServerResponse, withoutBody = headers.append("set-cookie", header) } } - if (withoutBody) { + if (options?.withoutBody) { return new Response(undefined, { status: response.status, statusText: response.statusText as string, @@ -544,7 +548,7 @@ export const toWeb = (response: ServerResponse.HttpServerResponse, withoutBody = }) } case "Stream": { - return new Response(Stream.toReadableStream(body.stream), { + return new Response(Stream.toReadableStreamRuntime(body.stream, options?.runtime ?? Runtime.defaultRuntime), { status: response.status, statusText: response.statusText, headers diff --git a/packages/platform/test/HttpApp.test.ts b/packages/platform/test/HttpApp.test.ts index 28457ec35c..496ef41fd5 100644 --- a/packages/platform/test/HttpApp.test.ts +++ b/packages/platform/test/HttpApp.test.ts @@ -1,5 +1,5 @@ import { HttpApp, HttpServerResponse } from "@effect/platform" -import { Stream } from "effect" +import { FiberRef, Runtime, Stream } from "effect" import { assert, describe, test } from "vitest" describe("Http/App", () => { @@ -34,5 +34,17 @@ describe("Http/App", () => { const response = await handler(new Request("http://localhost:3000/")) assert.strictEqual(await response.text(), "foobar") }) + + test("stream runtime", async () => { + const handler = HttpApp.toWebHandlerRuntime( + Runtime.defaultRuntime.pipe( + Runtime.setFiberRef(FiberRef.currentConcurrency, 420) + ) + )(HttpServerResponse.stream( + FiberRef.get(FiberRef.currentConcurrency).pipe(Stream.map(String), Stream.encodeText) + )) + const response = await handler(new Request("http://localhost:3000/")) + assert.strictEqual(await response.text(), "420") + }) }) })