Open History to see the last commit
1import * as Context from "effect/Context";
2import type * as Effect from "effect/Effect";
3import type * as Output from "./Output.ts";
4
5/**
6 * A host that receives HTTP deliveries from an external service, such as
7 * Stripe or GitHub webhooks. Event sources (`Stripe.ConsumeEventsLive`,
8 * `GitHub.RepositoryEventSourceLive`) are written against it, so they don't
9 * depend on the platform the host runs on.
10 *
11 * Hosts provide it themselves (see `RuntimeContext.hostServices`), so user
12 * code only provides the event source. A secret bound with {@link
13 * WebhookHostService.bind} is read back at runtime through `RuntimeContext`.
14 *
15 * TODO: only Cloudflare Workers provide it. Provide it from the other HTTP
16 * hosts (Lambda, Fly, Railway, ...).
17 */
18export interface WebhookHostService {
19 /** Logical ID of the host. Deploy-time resources are namespaced under it. */
20 readonly LogicalId: string;
21 /** The host's public base URL, without a trailing slash. */
22 readonly url: Output.Output<string | undefined>;
23 /**
24 * Attach environment variables to the host as a binding under `id`. Unlike
25 * a prop, a binding may depend on resources that depend on the host, such
26 * as a webhook endpoint pointing at its `url`.
27 */
28 readonly bind: (id: string, data: { env: Record<string, unknown> }) => Effect.Effect<void>;
29 /**
30 * Claim requests whose URL pathname is exactly `path` (no trailing-slash
31 * normalization). One handler per path; claiming a path twice is a defect.
32 * Requests on any other path fall through to the host's own handler.
33 */
34 readonly route: <Req>(
35 path: string,
36 handler: (request: Request) => Effect.Effect<Response, never, Req>,
37 ) => Effect.Effect<void, never, Req>;
38}
39
40export class WebhookHost extends Context.Service<WebhookHost, WebhookHostService>()(
41 "alchemy/WebhookHost",
42) {}