1import * as Effect from "effect/Effect";
2import { DEFAULT_COMPATIBILITY_DATE } from "./internal/constants.ts";
3import * as Plugin from "./Plugin.ts";
4import { Runtime } from "./Runtime.ts";
5
6class HttpServer extends Plugin.Service<HttpServer>()(
7 "cloudflare-runtime/plugin/HttpServer",
8) {}
9
10/**
11 * Publish an HTTP dev server under a Worker name for local service bindings.
12 * The scoped Worker forwards to a fixed upstream address through workerd's
13 * HTTP service, preserving the original request URL, headers, streaming body,
14 * and WebSocket upgrades. Its registry entry is removed on shutdown.
15 */
16export const registerHttpServer = (name: string, url: URL) =>
17 Effect.flatMap(Runtime, (runtime) =>
18 runtime
19 .start({
20 name,
21 compatibilityDate: DEFAULT_COMPATIBILITY_DATE,
22 compatibilityFlags: [],
23 cache: false,
24 bindings: [
25 Plugin.useSync(HttpServer, () => ({
26 name: "SERVER",
27 service: { name: "http-dev-server" },
28 })),
29 ],
30 modules: [
31 {
32 name: "http-dev-server.js",
33 type: "ESModule",
34 content: `export default {
35 fetch(request, env) { return env.SERVER.fetch(request); }
36 };`,
37 },
38 ],
39 })
40 .pipe(
41 Effect.provideService(
42 HttpServer,
43 HttpServer.of({
44 services: [
45 {
46 name: "http-dev-server",
47 external: {
48 address: `${url.hostname}:${url.port || (url.protocol === "https:" ? "443" : "80")}`,
49 ...(url.protocol === "https:"
50 ? {
51 https: {
52 options: {
53 forwardedProtoHeader: "X-Forwarded-Proto",
54 },
55 },
56 }
57 : { http: { forwardedProtoHeader: "X-Forwarded-Proto" } }),
58 },
59 },
60 ],
61 }),
62 ),
63 ),
64 );