import * as Context from "../Context.ts"; import * as Effect from "../Effect.ts"; import * as Layer from "../Layer.ts"; import * as Option from "../Option.ts"; import type { ReadonlyRecord } from "../Record.ts"; import * as Schema from "../Schema.ts"; import type { ParseOptions } from "../SchemaAST.ts"; import * as Scope from "../Scope.ts"; import type * as Types from "../Types.ts"; import * as FindMyWay from "./FindMyWay.ts"; import * as HttpIncomingMessage from "./HttpIncomingMessage.ts"; import type * as HttpMethod from "./HttpMethod.ts"; import * as HttpServer from "./HttpServer.ts"; import * as HttpServerError from "./HttpServerError.ts"; import * as HttpServerRequest from "./HttpServerRequest.ts"; import * as HttpServerResponse from "./HttpServerResponse.ts"; declare const TypeId = "~effect/http/HttpRouter"; /** * Defines the service interface for registering HTTP routes and middleware. * * **Details** * * An `HttpRouter` can add routes, apply path prefixes, install global middleware, * and expose the registered routes as an Effect that handles the current server * request. * * @stability unstable * @category services * @since 4.0.0 */ export interface HttpRouter { readonly [TypeId]: typeof TypeId; readonly prefixed: (prefix: string) => HttpRouter; readonly add: (method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "QUERY", path: PathInput, handler: HttpServerResponse.HttpServerResponse | Effect.Effect | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect), options?: { readonly uninterruptible?: boolean | undefined; } | undefined) => Effect.Effect> | Request.From<"Error", E>>; readonly addAll: >>(routes: Routes) => Effect.Effect, Provided>> | Request.From<"Error", Route.Error>>; readonly addGlobalMiddleware: (middleware: ((effect: Effect.Effect) => Effect.Effect) & (Types.unhandled extends E ? unknown : "You cannot handle any errors")) => Effect.Effect> | Request.From<"GlobalError", Exclude>>; readonly asHttpEffect: () => Effect.Effect; } /** * Service tag for the HTTP router used while constructing an HTTP application. * Route and middleware layers require this service to register themselves with * the router. * * @stability unstable * @category services * @since 4.0.0 */ export declare const HttpRouter: Context.Service; /** * Constructs an empty `HttpRouter` service. * * **Details** * * The returned router accepts route and middleware registrations and later routes * the current `HttpServerRequest` to the matching `HttpServerResponse`. * * @stability unstable * @category constructors * @since 4.0.0 */ export declare const make: Effect.Effect; /** * Context reference for low-level router configuration. * * **Details** * * The value is passed to the route matcher when an `HttpRouter` is created and * defaults to an empty configuration. * * @stability unstable * @category services * @since 4.0.0 */ export declare const RouterConfig: Context.Reference>; declare const RouteContext_base: Context.ServiceClass>; readonly route: Route; }>; /** * Service for the matched HTTP route in the current request. * * **When to use** * * Use to read captured path parameters and route metadata while handling a * request matched by the router. * * **Details** * * It provides the route definition and the path parameters captured by the route * matcher. * * @stability unstable * @category services * @since 4.0.0 */ export declare class RouteContext extends RouteContext_base { } /** * Effect that returns the path parameters captured for the current matched route. * * @stability unstable * @category getters * @since 4.0.0 */ export declare const params: Effect.Effect, never, RouteContext>; /** * Decodes a schema from the current request and its JSON body. * * **Details** * * The input passed to the schema includes the request method, URL, headers, * cookies, path parameters, search parameters, and parsed JSON body. The effect * fails if the body cannot be parsed or the schema decode fails. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const schemaJson: >; readonly headers: Readonly>; readonly pathParams: Readonly>; readonly searchParams: Readonly | undefined>>; readonly body: any; }>, RD>(schema: Schema.ConstraintCodec, options?: (ParseOptions & HttpIncomingMessage.JsonOptions) | undefined) => Effect.Effect; /** * Decodes a schema from the current request without reading the request body. * * **Details** * * The input passed to the schema includes the request method, URL, headers, * cookies, path parameters, and search parameters. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const schemaNoBody: >; readonly headers: Readonly>; readonly pathParams: Readonly>; readonly searchParams: Readonly | undefined>>; }>, RD>(schema: Schema.ConstraintCodec, options?: ParseOptions | undefined) => Effect.Effect; /** * Decodes a schema from the current route path parameters and search parameters. * * **Details** * * When the same key appears in both sources, the path parameter value is used. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const schemaParams: | undefined>>, RD>(schema: Schema.ConstraintCodec, options?: ParseOptions | undefined) => Effect.Effect; /** * Decodes a schema from the path parameters captured for the current matched * route. * * @stability unstable * @category schemas * @since 4.0.0 */ export declare const schemaPathParams: >, RD>(schema: Schema.ConstraintCodec, options?: ParseOptions | undefined) => Effect.Effect; /** * Creates a layer that accesses the current `HttpRouter` service and runs the * supplied effect. * * **When to use** * * Use when you need to register routes or middleware with the router during layer * construction. * * **Example** (Registering routes during layer construction) * * ```ts import.meta.vitest * import { Effect } from "effect" * import { HttpRouter, HttpServerResponse } from "effect/http" * * const Routes = HttpRouter.use((router) => * router.add("GET", "/health", HttpServerResponse.text("ready")) * ) * * const program = Effect.acquireUseRelease( * Effect.sync(() => HttpRouter.toWebHandler(Routes, { disableLogger: true })), * ({ handler }) => * Effect.gen(function*() { * const response = yield* Effect.promise(() => handler(new Request("http://localhost/health"))) * const body = yield* Effect.promise(() => response.text()) * return body * }), * ({ dispose }) => Effect.promise(dispose) * ) * * await Effect.runPromise(program) // => "ready" * ``` * * @stability unstable * @category layers * @since 4.0.0 */ export declare const use: (f: (router: HttpRouter) => Effect.Effect) => Layer.Layer>; /** * Create a layer that adds a single route to the HTTP router. * * **Example** (Adding a GET route) * * ```ts import.meta.vitest * import { Layer } from "effect" * import { HttpRouter, HttpServerResponse } from "effect/http" * * const Route = HttpRouter.add( * "GET", * "/hello", * HttpServerResponse.text("Hello, World!") * ) * Layer.isLayer(Route) // => true * ``` * * @stability unstable * @category layers * @since 4.0.0 */ export declare const add: (method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "QUERY", path: PathInput, handler: HttpServerResponse.HttpServerResponse | Effect.Effect | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect), options?: { readonly uninterruptible?: boolean | undefined; }) => Layer.Layer> | Request.From<"Error", E>>; /** * Create a layer that adds multiple routes to the HTTP router. * * **Example** (Adding multiple routes) * * ```ts import.meta.vitest * import { Layer } from "effect" * import { HttpRouter, HttpServerResponse } from "effect/http" * * const Routes = HttpRouter.addAll([ * HttpRouter.route( * "GET", * "/hello", * HttpServerResponse.text("Hello, World!") * ) * ]) * Layer.isLayer(Routes) // => true * ``` * * @stability unstable * @category layers * @since 4.0.0 */ export declare const addAll: >, EX = never, RX = never>(routes: Routes | Effect.Effect, options?: { readonly prefix?: string | undefined; }) => Layer.Layer | Request.From<"Requires", Exclude, Provided>> | Request.From<"Error", Route.Error>>; /** * Layer that provides a newly constructed `HttpRouter`. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const layer: Layer.Layer; /** * Builds an application layer with a router and returns the router as an HTTP * handler effect. * * **Details** * * The returned effect handles the current `HttpServerRequest` in the current * `Scope`; route request markers are converted into the ordinary requirements of * the returned handler. * * **Gotchas** * * The layer is built with a forked `MemoMap`, so calling `toHttpEffect` twice * will build the same layer twice. * * @stability unstable * @category converting * @since 4.0.0 */ export declare const toHttpEffect: (appLayer: Layer.Layer) => Effect.Effect | Request.Only<"GlobalError", R> | HttpServerError.HttpServerError, Scope.Scope | HttpServerRequest.HttpServerRequest | Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>>, Request.Without, Exclude, HttpRouter> | Scope.Scope>; declare const RouteTypeId = "~effect/http/HttpRouter/Route"; /** * Description of a registered HTTP route. * * **Details** * * A route pairs an HTTP method and path pattern with a response handler, plus * metadata used for prefix handling and interruptibility. * * @stability unstable * @category routes * @since 4.0.0 */ export interface Route { readonly [RouteTypeId]: typeof RouteTypeId; readonly method: HttpMethod.HttpMethod | "*"; readonly path: PathInput; readonly handler: Effect.Effect; readonly uninterruptible: boolean; readonly prefix: Option.Option; } /** * Helper types for extracting the error and context types carried by `Route` * values. * * @stability unstable * @since 4.0.0 */ export declare namespace Route { /** * Extracts the error type produced by a `Route` handler. * * @stability unstable * @category routes * @since 4.0.0 */ type Error> = R extends Route ? E : never; /** * Extracts the context requirements of a `Route` handler. * * @stability unstable * @category routes * @since 4.0.0 */ type Context> = T extends Route ? R : never; } /** * Constructs a `Route` from an HTTP method, path, and handler. * * **Details** * * The handler may be a static response, an effect that produces a response, or a * function from the current request to a response effect. Set `uninterruptible` to * prevent the route handler from being made interruptible while it runs. * * @stability unstable * @category routes * @since 4.0.0 */ export declare const route: (method: "*" | HttpMethod.HttpMethod, path: PathInput, handler: HttpServerResponse.HttpServerResponse | Effect.Effect | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect), options?: { readonly uninterruptible?: boolean | undefined; }) => Route>; /** * Path pattern accepted by the router. Routes must use an absolute path * beginning with `/` or the wildcard `*`. * * @stability unstable * @category models * @since 4.0.0 */ export type PathInput = `/${string}` | "*"; /** * Adds a path prefix to a route path. * * **Details** * * Trailing slashes are removed from the prefix; `/` becomes the prefix itself and * `*` becomes a wildcard route under the prefix. * * @stability unstable * @category transforming * @since 4.0.0 */ export declare const prefixPath: { /** * Adds a path prefix to a route path. * * **Details** * * Trailing slashes are removed from the prefix; `/` becomes the prefix itself and * `*` becomes a wildcard route under the prefix. * * @stability unstable * @category transforming * @since 4.0.0 */ (prefix: string): (self: string) => string; /** * Adds a path prefix to a route path. * * **Details** * * Trailing slashes are removed from the prefix; `/` becomes the prefix itself and * `*` becomes a wildcard route under the prefix. * * @stability unstable * @category transforming * @since 4.0.0 */ (self: string, prefix: string): string; }; /** * Returns a copy of a route with its path prefixed. * * **Details** * * The prefix is also tracked on the route so that, when the route handles a * request, the matched prefix can be removed from the request URL seen by the * handler. * * @stability unstable * @category routes * @since 4.0.0 */ export declare const prefixRoute: { /** * Returns a copy of a route with its path prefixed. * * **Details** * * The prefix is also tracked on the route so that, when the route handles a * request, the matched prefix can be removed from the request URL seen by the * handler. * * @stability unstable * @category routes * @since 4.0.0 */ (prefix: string): (self: Route) => Route; /** * Returns a copy of a route with its path prefixed. * * **Details** * * The prefix is also tracked on the route so that, when the route handles a * request, the matched prefix can be removed from the request URL seen by the * handler. * * @stability unstable * @category routes * @since 4.0.0 */ (self: Route, prefix: string): Route; }; /** * Represents a request-level dependency, that needs to be provided by * middleware. * * @stability unstable * @category utility types * @since 4.0.0 */ export interface Request { readonly _: unique symbol; readonly kind: Kind; readonly type: T; } /** * Helper types for request-level dependency markers used by router layers and * middleware. * * @stability unstable * @since 4.0.0 */ export declare namespace Request { /** * Wraps a type in a request-level marker of the supplied kind. * * @stability unstable * @category utility types * @since 4.0.0 */ type From = R extends infer T ? Request : never; /** * Extracts the payload types from request-level markers that have the supplied * kind. * * @stability unstable * @category utility types * @since 4.0.0 */ type Only = A extends Request ? T : never; /** * Removes request-level markers from a union, leaving only ordinary requirement * or error types. * * @stability unstable * @category utility types * @since 4.0.0 */ type Without = A extends Request ? never : A; } /** * Services provided by the HTTP router, which are available in the * request context. * * @stability unstable * @category utility types * @since 4.0.0 */ export type Provided = HttpServerRequest.HttpServerRequest | Scope.Scope | HttpServerRequest.ParsedSearchParams | RouteContext; /** * Services provided to global middleware. * * @stability unstable * @category utility types * @since 4.0.0 */ export type GlobalProvided = HttpServerRequest.HttpServerRequest | Scope.Scope; declare const MiddlewareTypeId = "~effect/http/HttpRouter/Middleware"; /** * Composable descriptor for route-scoped HTTP router middleware. * * **Details** * * Its `layer` can be provided to route layers, and `combine` composes middleware * while tracking provided services, handled errors, and remaining requirements at * the type level. * * @stability unstable * @category middleware * @since 4.0.0 */ export interface Middleware { readonly [MiddlewareTypeId]: Config; readonly layer: [Config["requires"]] extends [never] ? Layer.Layer | Request.From<"Error", Config["handles"]>, Config["layerError"], Config["layerRequires"] | Request.From<"Requires", Config["requires"]> | Request.From<"Error", Config["error"]>> : "Need to .combine(middleware) that satisfy the missing request dependencies"; readonly combine: (other: Middleware) => Middleware<{ provides: Config2["provides"] | Config["provides"]; handles: Config2["handles"] | Config["handles"]; error: Config2["error"] | Exclude; requires: Exclude | Config2["requires"]; layerError: Config["layerError"] | Config2["layerError"]; layerRequires: Config["layerRequires"] | Config2["layerRequires"]; }>; } /** * Create a middleware layer that can be used to modify requests and responses. * * **Details** * * By default, the middleware only affects the routes that it is provided to. * * If you want to create a middleware that applies globally to all routes, pass * the `global` option as `true`. * * **Example** (Applying route and global middleware) * * ```ts import.meta.vitest * import { Effect, Layer } from "effect" * import { HttpRouter, HttpServerResponse } from "effect/http" * * const RouteMiddleware = HttpRouter.middleware((httpEffect) => * Effect.map(httpEffect, HttpServerResponse.setHeader("x-route", "route")) * ).layer * * const GlobalMiddleware = HttpRouter.middleware( * (httpEffect) => Effect.map(httpEffect, HttpServerResponse.setHeader("x-global", "global")), * { global: true } * ) * * const Routes = HttpRouter.add("GET", "/hello", HttpServerResponse.text("Hello")).pipe( * Layer.provide(RouteMiddleware) * ) * const App = Layer.mergeAll(Routes, GlobalMiddleware) * * const program = Effect.acquireUseRelease( * Effect.sync(() => HttpRouter.toWebHandler(App, { disableLogger: true })), * ({ handler }) => * Effect.gen(function*() { * const response = yield* Effect.promise(() => handler(new Request("http://localhost/hello"))) * return [response.headers.get("x-route"), response.headers.get("x-global")] * }), * ({ dispose }) => Effect.promise(dispose) * ) * * await Effect.runPromise(program) // => ["route", "global"] * ``` * * @stability unstable * @category middleware * @since 4.0.0 */ export declare const middleware: middleware.Make & (() => middleware.Make); /** * Types used by the `middleware` constructor. * * @stability unstable * @since 4.0.0 */ export declare namespace middleware { /** * Overloaded constructor type for router middleware. * * **Details** * * It builds either a route-scoped `Middleware` or, when `global` is `true`, a * layer that installs middleware for all routes. The type tracks provided * services, handled errors, middleware failures, and remaining requirements. * * @stability unstable * @category middleware * @since 4.0.0 */ type Make = { (middleware: Effect.Effect<(effect: Effect.Effect, Types.NoInfer>) => Effect.Effect & (Types.unhandled extends E ? unknown : "You must only handle the configured errors"), EX, RX>, options?: { readonly global?: Global | undefined; }): Global extends true ? Layer.Layer | Request.From<"Error", Handles> | Request.From<"GlobalRequires", Provides> | Request.From<"GlobalError", Handles>, EX, HttpRouter | Exclude | Request.From<"GlobalRequires", Exclude> | Request.From<"GlobalError", Exclude>> : Middleware<{ provides: Provides; handles: Handles; error: Exclude; requires: Exclude; layerError: EX; layerRequires: Exclude; }>; (middleware: ((effect: Effect.Effect, Types.NoInfer>) => Effect.Effect) & (Types.unhandled extends E ? unknown : "You must only handle the configured errors"), options?: { readonly global?: Global | undefined; }): Global extends true ? Layer.Layer | Request.From<"Error", Handles> | Request.From<"GlobalRequires", Provides> | Request.From<"GlobalError", Handles>, never, HttpRouter | Request.From<"GlobalRequires", Exclude> | Request.From<"GlobalError", Exclude>> : Middleware<{ provides: Provides; handles: Handles; error: Exclude; requires: Exclude; layerError: never; layerRequires: never; }>; }; /** * Function that transforms an HTTP response effect into another HTTP response * effect. * * @stability unstable * @category middleware * @since 4.0.0 */ type Fn = (effect: Effect.Effect) => Effect.Effect; } /** * Middleware that applies CORS headers to the HTTP response. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const cors: (options?: { readonly allowedOrigins?: ReadonlyArray | undefined; readonly allowedMethods?: ReadonlyArray | undefined; readonly allowedHeaders?: ReadonlyArray | undefined; readonly exposedHeaders?: ReadonlyArray | undefined; readonly maxAge?: number | undefined; readonly credentials?: boolean | undefined; } | undefined) => Layer.Layer; /** * Middleware that disables the logger for some routes. * * **Example** (Disabling route logging) * * ```ts import.meta.vitest * import { Layer } from "effect" * import { HttpRouter, HttpServerResponse } from "effect/http" * * const Route = HttpRouter.add( * "GET", * "/hello", * HttpServerResponse.text("Hello, World!") * ).pipe( * // disable the logger for this route * Layer.provide(HttpRouter.disableLogger) * ) * Layer.isLayer(Route) // => true * ``` * * @stability unstable * @category layers * @since 4.0.0 */ export declare const disableLogger: Layer.Layer; /** * Provides request-level dependencies to some routes. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const provideRequest: (layer: Layer.Layer) => (self: Layer.Layer) => Layer.Layer>>; /** * Runs the provided application layer as an HTTP server. * * **Details** * * Layers first built inside the app are private to this server. Provide * services that must be shared with sibling layers outside `serve`. * * **Gotchas** * * The layer is built with a forked `MemoMap`, so calling `serve` twice will * build the same layer twice. * * @stability unstable * @category layers * @since 4.0.0 */ export declare const serve: | Request.Only<"GlobalRequires", R>>(appLayer: Layer.Layer, options?: { readonly routerConfig?: Partial | undefined; readonly disableLogger?: boolean | undefined; readonly disableListenLog?: boolean; /** * Middleware to apply to the HTTP server. * * **Gotchas** * * This middleware is applied to the entire HTTP server chain, including the * sending of the response. Changes to the response are not reflected in the * final response sent to the client. Use `HttpRouter.middleware` when * middleware must modify the response. */ readonly middleware?: (effect: Effect.Effect | Request.Only<"GlobalError", R> | HttpServerError.HttpServerError, Scope.Scope | HttpServerRequest.HttpServerRequest | Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>>) => Effect.Effect; }) => Layer.Layer, HttpServer.HttpServer | Exclude | Exclude, HttpRouter>>; /** * Builds a Fetch-compatible request handler from an HTTP router application * layer. * * **Details** * * The result contains a `handler` function that converts Web `Request` values to * Web `Response` values and a `dispose` function for releasing the layer * resources. The layer is built immediately, so the cost is paid when the * handler is created rather than on the first request. A layer that performs * asynchronous work while building may still be in progress when the first * request arrives, in which case that request waits for the build to finish. * If the build fails, every request rejects with the build error. * * **Gotchas** * * On Cloudflare Workers, build the handler at module scope: fibers started * inside `fetch` capture that request's async context. Layer construction * must not require request-time I/O; bindings are available from * `cloudflare:workers` via its `env` export. * Prefer request-local work or a Durable Object's supported lifecycle APIs, * not indefinite background execution. For shared fibers, provide a sync * scheduler to both layers and requests as below; see `Scheduler.MixedScheduler` * for its limitations. Bound waits for shared work with request-local timeouts. * * **Example** (Opting in to the sync scheduler on Workers) * * ```ts * import { Effect, Layer, Scheduler } from "effect" * import { HttpRouter, HttpServer, HttpServerResponse } from "effect/http" * * const SyncScheduler = Layer.succeed( * Scheduler.Scheduler, * new Scheduler.MixedScheduler("sync") * ) * const AppLive = HttpRouter.add( * "GET", * "/ping", * Effect.succeed(HttpServerResponse.text("pong")) * ).pipe(Layer.provide(HttpServer.layerServices)) * * // Build once at module scope. The scheduler is also retained for requests. * const app = HttpRouter.toWebHandler( * AppLive.pipe(Layer.provideMerge(SyncScheduler)) * ) * * export default { fetch: (request: Request) => app.handler(request) } * ``` * * @stability unstable * @category converting * @since 4.0.0 */ export declare const toWebHandler: | Request<"GlobalRequires", any> | Request<"Error", any> | Request<"GlobalError", any>, HE, HR = Exclude | Request.Only<"GlobalRequires", R>, A>, ReqR = Exclude>(appLayer: Layer.Layer, options?: { readonly memoMap?: Layer.MemoMap | undefined; readonly routerConfig?: Partial | undefined; readonly disableLogger?: boolean | undefined; /** * Middleware to apply to the HTTP server. * * **Gotchas** * * This middleware is applied to the entire HTTP server chain, including the * sending of the response. Changes to the response are not reflected in the * final response sent to the client. Use `HttpRouter.middleware` when * middleware must modify the response. */ readonly middleware?: (effect: Effect.Effect | Request.Only<"GlobalError", R> | HttpServerError.HttpServerError, Scope.Scope | HttpServerRequest.HttpServerRequest | Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>>) => Effect.Effect; }) => { readonly handler: [ReqR] extends [never] ? ((request: globalThis.Request, context?: Context.Context | undefined) => Promise) : ((request: globalThis.Request, context: Context.Context) => Promise); readonly dispose: () => Promise; }; export {}; //# sourceMappingURL=HttpRouter.d.ts.map