UNPKG

h3

Version:

Minimal H(TTP) framework built for high performance and portability.

1,628 lines 73.2 kB
import { CookieSerializeOptions, DynamicEventHandler, ErrorDetails, EventHandler, EventHandlerObject, EventHandlerRequest, EventHandlerResponse, EventHandlerWithFetch, FetchableObject, H3, H3$1, H3Config, H3Event, H3EventContext, H3Plugin, H3RouteMeta, HTTPError, HTTPEvent, HTTPHandler, HTTPMethod, HTTPResponse, InferEventInput, MaybePromise as MaybePromise$1, Middleware, ProxyOptions, ResolvedRequest } from "./h3.mjs";
import { NodeServerRequest, NodeServerResponse, ServerRequest, ServerRequestContext } from "srvx";
import { Hooks, Hooks as WebSocketHooks, Message as WebSocketMessage, Peer, Peer as WebSocketPeer } from "crossws";

export declare function isEvent(input: any): input is H3Event;
/**
 * Checks if the input is an object with `{ req: Request }` signature.
 * @param input - The input to check.
 * @returns True if the input is `{ req: Request }`
 */
export declare function isHTTPEvent(input: any): input is HTTPEvent;
/**
 * Gets the context of the event, if it does not exists, initializes a new context on `req.context`.
 */
export declare function getEventContext<T extends ServerRequestContext | H3EventContext>(event: HTTPEvent | H3Event): T;
export declare function mockEvent(_request: string | URL | Request, options?: RequestInit & {
  h3?: H3EventContext;
}): H3Event;
/** The Standard Schema interface. */
interface StandardSchemaV1<Input = unknown, Output = Input> {
  /** The Standard Schema properties. */
  readonly "~standard": Props<Input, Output>;
}
/** The Standard Schema properties interface. */
interface Props<Input = unknown, Output = Input> {
  /** The version number of the standard. */
  readonly version: 1;
  /** The vendor name of the schema library. */
  readonly vendor: string;
  /** Validates unknown input values. */
  readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
  /** Inferred types associated with the schema. */
  readonly types?: Types<Input, Output> | undefined;
}
/** The result interface of the validate function. */
type Result<Output> = SuccessResult<Output> | FailureResult;
/** The result interface if validation succeeds. */
interface SuccessResult<Output> {
  /** The typed output value. */
  readonly value: Output;
  /** The non-existent issues. */
  readonly issues?: undefined;
}
/** The result interface if validation fails. */
interface FailureResult {
  /** The issues of failed validation. */
  readonly issues: ReadonlyArray<Issue>;
}
/** The issue interface of the failure output. */
interface Issue {
  /** The error message of the issue. */
  readonly message: string;
  /** The path of the issue, if any. */
  readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
}
/** The path segment interface of the issue. */
interface PathSegment {
  /** The key representing a path segment. */
  readonly key: PropertyKey;
}
/** The Standard Schema types interface. */
interface Types<Input = unknown, Output = Input> {
  /** The input type of the schema. */
  readonly input: Input;
  /** The output type of the schema. */
  readonly output: Output;
}
/** Infers the output type of a Standard Schema. */
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
type ValidateResult<T> = T | true | false | void;
type OnValidateError<Source extends string = string> = (result: FailureResult & {
  _source?: Source;
}) => ErrorDetails;
export declare function defineHandler<Req extends EventHandlerRequest = EventHandlerRequest, Res = EventHandlerResponse>(handler: EventHandler<Req, Res>): EventHandlerWithFetch<Req, Res>;
export declare function defineHandler<Req extends EventHandlerRequest = EventHandlerRequest, Res = EventHandlerResponse>(def: EventHandlerObject<Req, Res>): EventHandlerWithFetch<Req, Res>;
type StringHeaders<T> = { [K in keyof T]: Extract<T[K], string>; };
type ValidatedRequest<RequestBody extends StandardSchemaV1, RequestHeaders extends StandardSchemaV1, RequestQuery extends StandardSchemaV1> = {
  body: InferOutput<RequestBody>;
  headers: StringHeaders<InferOutput<RequestHeaders>>;
  query: StringHeaders<InferOutput<RequestQuery>>;
};
/**
 * @experimental defineValidatedHandler is an experimental feature and API may change.
 */
export declare function defineValidatedHandler<RequestBody extends StandardSchemaV1, RequestHeaders extends StandardSchemaV1, RequestQuery extends StandardSchemaV1, Res extends EventHandlerResponse = EventHandlerResponse>(def: Omit<EventHandlerObject, "handler"> & {
  validate?: {
    body?: RequestBody;
    headers?: RequestHeaders;
    query?: RequestQuery;
    onError?: OnValidateError;
  };
  handler: EventHandler<ValidatedRequest<RequestBody, RequestHeaders, RequestQuery>, Res>;
}): EventHandlerWithFetch<ValidatedRequest<RequestBody, RequestHeaders, RequestQuery>, Res>;
export declare function dynamicEventHandler(initial?: EventHandler | FetchableObject): DynamicEventHandler;
type MaybePromise<T> = T | Promise<T>;
export declare function defineLazyEventHandler<_RequestT extends EventHandlerRequest = EventHandlerRequest>(loader: () => MaybePromise<HTTPHandler<_RequestT>>): EventHandlerWithFetch<ResolvedRequest<_RequestT>>;
export declare function toEventHandler<_RequestT extends EventHandlerRequest = EventHandlerRequest>(handler: HTTPHandler<_RequestT> | undefined): EventHandler<ResolvedRequest<_RequestT>> | undefined;
export type NodeHandler = (req: NodeServerRequest, res: NodeServerResponse) => unknown | Promise<unknown>;
export type NodeMiddleware = (req: NodeServerRequest, res: NodeServerResponse, next: (error?: Error) => void) => unknown | Promise<unknown>;
/**
 * @deprecated Since h3 v2 you can directly use `app.fetch(request, init?, context?)`
 */
export declare function toWebHandler(app: H3): (request: ServerRequest, context?: H3EventContext) => Promise<Response>;
export declare function fromWebHandler(handler: (request: ServerRequest, context?: H3EventContext) => Promise<Response>): EventHandler;
/**
 * Convert a Node.js handler function (req, res, next?) to an EventHandler.
 *
 * **Note:** The returned event handler requires to be executed with h3 Node.js handler.
 */
export declare function fromNodeHandler(handler: NodeMiddleware): EventHandler;
export declare function fromNodeHandler(handler: NodeHandler): EventHandler;
export declare function defineNodeHandler(handler: NodeHandler): NodeHandler;
export declare function defineNodeMiddleware(handler: NodeMiddleware): NodeMiddleware;
/**
 * Route definition options
 */
export interface RouteDefinition {
  /**
   * HTTP method for the route, e.g. 'GET', 'POST', etc.
   */
  method: HTTPMethod;
  /**
   * Route pattern, e.g. '/api/users/:id'
   */
  route: string;
  /**
   * Handler function for the route.
   */
  handler: EventHandler;
  /**
   * Optional middleware to run before the handler.
   */
  middleware?: Middleware[];
  /**
   * Additional route metadata.
   */
  meta?: H3RouteMeta;
  validate?: {
    body?: StandardSchemaV1;
    headers?: StandardSchemaV1;
    query?: StandardSchemaV1;
  };
}
/**
 * Define a route as a plugin that can be registered with app.register()
 *
 * @example
 * ```js
 * import { z } from "zod";
 *
 * const userRoute = defineRoute({
 *    method: 'POST',
 *    validate: {
 *      query: z.object({ id: z.string().uuid() }),
 *      body: z.object({ name: z.string() }),
 *    },
 *    handler: (event) => {
 *      return { success: true };
 *    }
 * });
 *
 * app.register(userRoute);
 * ```
 */
export declare function defineRoute(def: RouteDefinition): H3Plugin;
/**
 * Remove a route handler from the app.
 *
 * All registrations matching `method` + `route` are removed (an empty `method`
 * only matches routes registered with `app.all()`).
 *
 * @example
 * ```ts
 * import { H3, removeRoute } from "h3";
 *
 * const app = new H3();
 * app.get("/temp", () => "hello");
 *
 * removeRoute(app, "GET", "/temp"); // route removed
 * ```
 */
export declare function removeRoute(app: H3$1, method: HTTPMethod | Lowercase<HTTPMethod> | "", route: string): void;
/**
 * Create a lightweight request proxy that overrides only the URL.
 *
 * Avoids cloning the original request (no `new Request()` allocation).
 */
export declare function requestWithURL(req: ServerRequest, url: string): ServerRequest;
/**
 * Create a lightweight request proxy with the base path stripped from the URL pathname.
 *
 * `options.url` is the parsed request URL to strip `base` from, in place of
 * parsing `req.url`. Pass `event.url` whenever there is an event: for a
 * non-canonical path it holds the canonicalized form the parent matched `base`
 * against, while `req.url` still holds the wire form, and slicing one by an
 * offset derived from the other is how mount prefixes desync.
 */
export declare function requestWithBaseURL(req: ServerRequest, base: string, options?: {
  url?: URL;
}): ServerRequest;
/**
 * Convert input into a web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
 *
 * If input is a relative URL, it will be normalized into a full path based on the `host` header.
 *
 * If input is already a Request and no options are provided, it will be returned as-is.
 *
 * **Security:** The `host` header is client input. It is only used as the authority of the
 * synthesized URL (falling back to `localhost` when absent or malformed) and can never widen
 * into the path, and `x-forwarded-proto` is ignored, so the scheme is always `http`. Pass an
 * absolute URL to control the origin.
 */
export declare function toRequest(input: ServerRequest | URL | string, options?: RequestInit): ServerRequest;
/**
 * Get parsed query string object from the request URL.
 *
 * @example
 * app.get("/", (event) => {
 *   const query = getQuery(event); // { key: "value", key2: ["value1", "value2"] }
 * });
 */
export declare function getQuery<T, Event extends H3Event | HTTPEvent = HTTPEvent, _T = Exclude<InferEventInput<"query", Event, T>, undefined>>(event: Event): _T;
export declare function getValidatedQuery<Event extends HTTPEvent, S extends StandardSchemaV1<any, any>>(event: Event, validate: S, options?: {
  onError?: (result: FailureResult) => ErrorDetails;
}): Promise<InferOutput<S>>;
export declare function getValidatedQuery<Event extends HTTPEvent, OutputT, InputT = InferEventInput<"query", Event, OutputT>>(event: Event, validate: (data: InputT) => ValidateResult<OutputT> | Promise<ValidateResult<OutputT>>, options?: {
  onError?: () => ErrorDetails;
}): Promise<OutputT>;
/**
 * Get matched route params.
 *
 * By default params are returned exactly as they appeared in the URL path, still
 * percent-encoded.
 *
 * With `decode: true` each param is decoded **once** (like `decodeURIComponent`),
 * except encoded path separators (`%2f`, `%5c`, at any `%25`-nesting depth) which
 * are left in their encoded form so decoding can never reintroduce a `/` or `\`
 * the router never matched.
 *
 * A single decode is not the same as "fully decoded": `%25XX` decodes to the
 * literal text `%XX`, so the result can still contain percent-escapes — including
 * dot segments (`%252e%252e` -> `%2e%2e`) and control characters (`%2500` -> `%00`).
 * **Do not decode the result again**: a second pass turns those back into
 * traversal (`../`) and separators the routing and middleware layers never saw.
 * Treat the returned string as final and validate it as-is.
 *
 * @example
 * app.get("/", (event) => {
 *   const params = getRouterParams(event); // { key: "value" }
 * });
 *
 * @example
 * // GET /files/%252e%252e/x
 * app.get("/files/**:rest", (event) => {
 *   getRouterParams(event); // { rest: "%252e%252e/x" }
 *   getRouterParams(event, { decode: true }); // { rest: "%2e%2e/x" } — still encoded, do not decode again
 * });
 */
export declare function getRouterParams(event: HTTPEvent, opts?: {
  decode?: boolean;
}): NonNullable<H3Event["context"]["params"]>;
export declare function getValidatedRouterParams<Event extends HTTPEvent, S extends StandardSchemaV1>(event: Event, validate: S, options?: {
  decode?: boolean;
  onError?: (result: FailureResult) => ErrorDetails;
}): Promise<InferOutput<S>>;
export declare function getValidatedRouterParams<Event extends HTTPEvent, OutputT, InputT = InferEventInput<"routerParams", Event, OutputT>>(event: Event, validate: (data: InputT) => ValidateResult<OutputT> | Promise<ValidateResult<OutputT>>, options?: {
  decode?: boolean;
  onError?: () => ErrorDetails;
}): Promise<OutputT>;
/**
 * Get a matched route param by name.
 *
 * If `decode` option is `true`, it will decode the matched route param (like
 * `decodeURIComponent`), except encoded path separators (`%2f`, `%5c`) are kept
 * encoded so decoding can never reintroduce a `/` or `\` the router never matched.
 *
 * @example
 * app.get("/", (event) => {
 *   const param = getRouterParam(event, "key");
 * });
 */
export declare function getRouterParam(event: HTTPEvent, name: string, opts?: {
  decode?: boolean;
}): string | undefined;
/**
 *
 * Checks if the incoming request method is of the expected type.
 *
 * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
 *
 * @example
 * app.get("/", (event) => {
 *   if (isMethod(event, "GET")) {
 *     // Handle GET request
 *   } else if (isMethod(event, ["POST", "PUT"])) {
 *     // Handle POST or PUT request
 *   }
 * });
 */
export declare function isMethod(event: HTTPEvent, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): boolean;
/**
 * Asserts that the incoming request method is of the expected type using `isMethod`.
 *
 * If the method is not allowed, it will throw a 405 error and include an `Allow`
 * response header listing the permitted methods, as required by RFC 9110.
 *
 * If `allowHead` is `true`, it will allow `HEAD` requests to pass if the expected method is `GET`.
 *
 * @example
 * app.get("/", (event) => {
 *   assertMethod(event, "GET");
 *   // Handle GET request, otherwise throw 405 error
 * });
 */
export declare function assertMethod(event: HTTPEvent, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): void;
/**
 * Get the request hostname.
 *
 * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
 *
 * If no host header is found, it will return an empty string.
 *
 * **Security:** The returned host reflects the client-supplied `Host` (or
 * `X-Forwarded-Host`) header and can be spoofed. Do not trust it for security
 * decisions (CSRF/origin checks, cache keys, generating absolute links sent to
 * other users) unless the `Host` value is pinned or validated upstream (e.g. an
 * allow-list of expected hosts, or a reverse proxy that overwrites it).
 *
 * @example
 * app.get("/", (event) => {
 *   const host = getRequestHost(event); // "example.com"
 * });
 */
export declare function getRequestHost(event: HTTPEvent, opts?: {
  xForwardedHost?: boolean;
}): string;
/**
 * Get the request protocol.
 *
 * If `xForwardedProto` is `true`, it will use the `x-forwarded-proto` header if it exists. When the header contains a comma-separated list of protocols, the first entry is used.
 *
 * Note: This header is opt-in (default `false`) since it can be spoofed by clients. Only enable it when your application runs behind a trusted reverse proxy or CDN that sets this header. This default was changed to match `getRequestHost` (`xForwardedHost`) and `getRequestIP` (`xForwardedFor`).
 *
 * If protocol cannot be determined, it will default to "http".
 *
 * @example
 * app.get("/", (event) => {
 *   const protocol = getRequestProtocol(event); // "https"
 * });
 */
export declare function getRequestProtocol(event: HTTPEvent | H3Event, opts?: {
  xForwardedProto?: boolean;
}): "http" | "https" | (string & {});
/**
 * Generated the full incoming request URL.
 *
 * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
 *
 * If `xForwardedProto` is `true`, it will use the `x-forwarded-proto` header if it exists.
 *
 * **Security:** The `.origin` and `.host` of the returned URL are derived from the
 * client-supplied `Host` (or `X-Forwarded-Host`) header and can be spoofed. Do not
 * trust them for security decisions (CSRF/origin checks, cache keys, generating
 * absolute links sent to other users) unless the `Host` value is pinned or
 * validated upstream (e.g. an allow-list of expected hosts, or a reverse proxy
 * that overwrites it). The `.pathname` and `.search` are not derived from the
 * spoofable host, but remain untrusted client input — validate or encode them for
 * their eventual sink (e.g. filesystem lookups, HTML output, downstream queries).
 *
 * @example
 * app.get("/", (event) => {
 *   const url = getRequestURL(event); // "https://example.com/path"
 * });
 */
export declare function getRequestURL(event: HTTPEvent | H3Event, opts?: {
  xForwardedHost?: boolean;
  xForwardedProto?: boolean;
}): URL;
/**
 * Try to get the client IP address from the incoming request.
 *
 * By default the address comes from `event.req.ip`: the connection peer, or the
 * client resolved from the forwarded chain when the server is configured to
 * trust an upstream proxy (e.g. srvx's `trustProxy`).
 *
 * If `xForwardedFor` is `true`, the **first** entry of the `x-forwarded-for`
 * header is returned instead, when the header exists.
 *
 * If IP cannot be determined, it will default to `undefined`.
 *
 * **Security:** `xForwardedFor` is opt-in because that first entry is client
 * input. Proxies conventionally *append* to the chain (nginx
 * `$proxy_add_x_forwarded_for`, most CDNs, and h3's own {@link proxy} util), so
 * a value sent by the client stays at the left of the chain and is exactly what
 * this returns — letting any caller choose their own address and defeat IP
 * allow-lists, rate limiting, geo checks, and audit logs. Enabling it also
 * *overrides* `event.req.ip`, discarding an address the server already resolved
 * correctly. Prefer configuring the server to trust your proxy (srvx
 * `trustProxy` walks the chain from the right, past trusted hops) and leave this
 * option off; only enable it when an upstream you control always overwrites
 * `x-forwarded-for` on every request.
 *
 * @example
 * app.get("/", (event) => {
 *   const ip = getRequestIP(event); // "192.0.2.0"
 * });
 */
export declare function getRequestIP(event: HTTPEvent, opts?: {
  /**
   * Return the first entry of the `X-Forwarded-For` HTTP header set by proxies.
   *
   * Note: only enable this when an upstream you control *overwrites* the
   * header. A proxy that appends to it (the common default) leaves a
   * client-sent value first, making the result spoofable. Prefer a trusted
   * proxy configured on the server (srvx `trustProxy`) with `event.req.ip`.
   */
  xForwardedFor?: boolean;
}): string | undefined;
type IterationSource<Val, Ret = Val> = Iterable<Val> | AsyncIterable<Val> | Iterator<Val, Ret | undefined> | AsyncIterator<Val, Ret | undefined> | (() => Iterator<Val, Ret | undefined> | AsyncIterator<Val, Ret | undefined>);
type IteratorSerializer<Value> = (value: Value) => Uint8Array | undefined;
export type DisposeCallback = (reason?: unknown) => unknown;
/**
 * Register a callback that runs once the event is fully over: the response body finished streaming, the client disconnected, or the body errored — on every runtime, not just Node.js.
 *
 * The callback receives `undefined` on normal completion, or the cancel/abort reason otherwise. Callbacks run in registration order after the global `onResponse` hook; sync throws and async rejections are absorbed (reported via `console.error` unless the app is configured with `silent`), and pending async callbacks are passed to `waitUntil`.
 *
 * Registering after disposal invokes the callback immediately. Registration is only guaranteed to observe the end of the event when made during request handling (handler, middleware, or `onResponse`).
 *
 * Note: this signals _"h3 is done with this event"_, not _"the client received the response"_ — for non-streaming bodies on non-Node.js runtimes it fires when the response is handed to the runtime. To react to a client disconnect _while still producing_ the response (for example to abort an upstream fetch), use `event.req.signal` instead.
 *
 * @example
 * app.get("/sse", (event) => {
 *   const interval = setInterval(() => {}, 1000);
 *   onDispose(event, () => clearInterval(interval));
 *   // ... return a streaming response
 * });
 */
export declare function onDispose(event: H3Event, cb: DisposeCallback): void;
/**
 * Respond with an empty payload.<br>
 *
 * @example
 * app.get("/", () => noContent());
 *
 * @param status status code to be send. By default, it is `204 No Content`.
 */
export declare function noContent(status?: number): HTTPResponse;
/**
 * Send a redirect response to the client.
 *
 * It adds the `location` header to the response and sets the status code to 302 by default.
 *
 * In the body, it sends a simple HTML page with a meta refresh tag to redirect the client in case the headers are ignored.
 *
 * **Security:** If `location` derives from user input (query params, form fields,
 * headers, etc.), validate it against an allow-list of permitted destinations
 * before redirecting. Passing user-controlled values through unchecked creates an
 * open redirect vulnerability. Prefer `redirectBack` for "return to previous page"
 * flows, which only honors same-origin referers.
 *
 * @example
 * app.get("/", () => {
 *   return redirect("https://example.com");
 * });
 *
 * @example
 * app.get("/", () => {
 *   return redirect("https://example.com", 301); // Permanent redirect
 * });
 */
export declare function redirect(location: string, status?: number, statusText?: string): HTTPResponse;
/**
 * Redirect the client back to the previous page using the `referer` header.
 *
 * If the `referer` header is missing or is a different origin, it falls back to the provided URL (default `"/"`).
 *
 * By default, only the **pathname** of the referer is used (query string and hash are stripped)
 * to prevent spoofed referers from carrying unintended parameters. Set `allowQuery: true` to preserve the query string.
 *
 * **Security:** The `fallback` value MUST be a trusted, hardcoded path — never use user input.
 * Passing user-controlled values (e.g., query params) as `fallback` creates an open redirect vulnerability.
 *
 * @example
 * app.post("/submit", (event) => {
 *   // process form...
 *   return redirectBack(event, { fallback: "/form" });
 * });
 */
export declare function redirectBack(event: H3Event, opts?: {
  /** Fallback URL when referer is missing or cross-origin (default: `"/"`). **Must be a trusted, hardcoded path — never user input.** */
  fallback?: string;
  /** HTTP status code for the redirect (default: `302`). */
  status?: number;
  /** Preserve the query string from the referer URL (default: `false`). */
  allowQuery?: boolean;
}): HTTPResponse;
/**
 * Write `HTTP/1.1 103 Early Hints` to the client.
 *
 * In runtimes that don't support early hints natively, this function
 * falls back to setting response headers which can be used by CDN.
 */
export declare function writeEarlyHints(event: H3Event, hints: Record<string, string | string[]>): void | Promise<void>;
/**
 * Iterate a source of chunks and send back each chunk in order.
 * Supports mixing async work together with emitting chunks.
 *
 * Each chunk must be a string or a buffer.
 *
 * For generator (yielding) functions, the returned value is treated the same as yielded values.
 *
 * The first chunk is awaited before the response is created, so status and headers staged while
 * producing it (`event.res.status`, `event.res.headers`) are still applied. Everything set after
 * the first chunk is ignored — headers are already on the wire by then. (Returning a raw
 * `ReadableStream` gives no such window: its response is created before the stream is read.)
 *
 * @param iterable - Iterator that produces chunks of the response.
 * @param serializer - Function that converts values from the iterable into stream-compatible values.
 * @template Value - Test
 *
 * @example
 * return iterable(async function* work() {
 *   // Open document body
 *   yield "<!DOCTYPE html>\n<html><body><h1>Executing...</h1><ol>\n";
 *   // Do work ...
 *   for (let i = 0; i < 1000; i++) {
 *     await delay(1000);
 *     // Report progress
 *     yield `<li>Completed job #`;
 *     yield i;
 *     yield `</li>\n`;
 *   }
 *   // Close out the report
 *   return `</ol></body></html>`;
 * });
 * async function delay(ms) {
 *   return new Promise((resolve) => setTimeout(resolve, ms));
 * }
 */
export declare function iterable<Value = unknown, Return = unknown>(iterable: IterationSource<Value, Return>, options?: {
  serializer: IteratorSerializer<Value | Return>;
}): Promise<HTTPResponse>;
/**
 * Respond with HTML content.
 *
 * When used as a **tagged template**, interpolated values are automatically
 * HTML-escaped (`& < > " '`) to help prevent XSS. Wrap a value with {@link raw}
 * to opt out of escaping for trusted markup.
 *
 * When called with a **plain string**, the whole string is HTML-escaped and
 * rendered as text. If escaping changes the input, a warning is logged — use
 * the tagged template for dynamic values, or pass trusted markup with
 * {@link raw}: `html(raw(markup))`.
 *
 * Escaping protects values in element content and inside quoted attribute
 * values only. It cannot make unquoted attributes, URL attributes (e.g.
 * `href` with a `javascript:` URL) or `<script>`/`<style>` contents safe —
 * validate such values separately.
 *
 * @example
 * // Tagged template (interpolations are escaped):
 * app.get("/", () => html`<h1>Hello, ${name}!</h1>`);
 *
 * @example
 * // Trusted markup (used as-is, not escaped):
 * app.get("/", () => html(raw("<h1>Hello, World!</h1>")));
 *
 * @example
 * // Opt out of escaping for a trusted interpolation:
 * app.get("/", () => html`<div>${raw(trustedMarkup)}</div>`);
 */
export declare function html(strings: TemplateStringsArray, ...values: unknown[]): HTTPResponse;
export declare function html(markup: string | RawHTML): HTTPResponse;
/**
 * Mark a string as trusted, pre-escaped HTML so it is used by the
 * {@link html} util **without** being escaped.
 *
 * Only use this for markup you fully control — passing user input to `raw`
 * re-introduces XSS risk.
 *
 * @example
 * // `heading` is trusted markup; `userName` is escaped automatically.
 * app.get("/", () => html`<div>${raw(heading)}<span>${userName}</span></div>`);
 *
 * @example
 * // Send a trusted markup string as-is:
 * app.get("/", () => html(raw("<h1>Hello, World!</h1>")));
 */
export declare function raw(value: string): RawHTML;
/** Trusted raw HTML wrapper produced by {@link raw}. */
export interface RawHTML {
  readonly value: string;
}
/**
 * Advertise the query formats a resource accepts by setting the `Accept-Query`
 * response header (RFC 10008, HTTP `QUERY` method).
 *
 * The media types are serialized as a
 * [Structured Fields](https://www.rfc-editor.org/rfc/rfc8941) List: the base
 * media type becomes a token and any `;name=value` parameters are emitted with
 * their values as quoted strings.
 *
 * @example
 * app.query("/search", (event) => {
 *   appendAcceptQuery(event, ["application/sql;charset=UTF-8", "application/jsonpath"]);
 *   // Accept-Query: application/sql;charset="UTF-8", application/jsonpath
 *   return handleSearch(event);
 * });
 *
 * @param event The H3Event passed by the handler.
 * @param mediaTypes A media type (with optional parameters) or an array of them.
 */
export declare function appendAcceptQuery(event: H3Event, mediaTypes: string | string[]): void;
/**
 * Assert that the request `Content-Type` is present and one of the accepted
 * media types, following the requirements of RFC 10008 for the HTTP `QUERY`
 * method.
 *
 * Throws:
 *
 * - `400 Bad Request` if the `Content-Type` header is missing.
 *
 * - `422 Unprocessable Content` if the `Content-Type` header is malformed.
 *
 * - `415 Unsupported Media Type` if the media type is not accepted.
 *
 * Accepted types may use wildcards: `*` / `*&#47;*` match anything and
 * `type/*` matches any subtype of `type`.
 *
 * @example
 * app.query("/search", async (event) => {
 *   requireContentType(event, ["application/sql", "application/jsonpath"]);
 *   const body = await readBody(event, { type: "text" });
 *   // ...
 * });
 *
 * @param event The HTTPEvent passed by the handler.
 * @param acceptedTypes An accepted media type or an array of them.
 * @returns The matched request media type (lower-cased, without parameters).
 */
export declare function requireContentType(event: HTTPEvent, acceptedTypes: string | string[]): string;
/**
 * Define a middleware that runs on each request.
 */
export declare function onRequest(hook: (event: H3Event) => MaybePromise$1<void>): Middleware;
/**
 * Define a middleware that runs after Response is generated.
 *
 * You can return a new Response from the handler to replace the original response.
 */
export declare function onResponse(hook: (response: Response, event: H3Event) => unknown): Middleware;
/**
 * Define a middleware that runs when an error occurs.
 *
 * You can return a new Response from the handler to gracefully handle the error.
 */
export declare function onError(hook: (error: HTTPError, event: H3Event) => unknown): Middleware;
/**
 * Define a middleware that limits the request body size to the specified limit.
 *
 * The limit is enforced as the body is read (see {@link assertBodySize}), so an
 * oversized body surfaces as a `413` Request Entity Too Large error when the
 * handler consumes it (an honest oversized `Content-Length` is still rejected
 * up-front). A body the handler never reads is not counted. If you need custom
 * handling, use `assertBodySize` directly.
 *
 * @param limit Body size limit in bytes
 * @see {assertBodySize}
 */
export declare function bodyLimit(limit: number): Middleware;
export interface ReadBodyOptions {
  /**
   * Force a parser instead of inferring it from the request `Content-Type`.
   *
   * - `"json"` (default): parse as JSON.
   * - `"text"`: return the raw string body.
   * - `"urlencoded"`: parse as `application/x-www-form-urlencoded`.
   * - `"formData"`: parse as `multipart/form-data` (or url-encoded) form data.
   */
  type?: "json" | "text" | "urlencoded" | "formData";
}
/**
 * Reads request body and tries to parse using JSON.parse or URLSearchParams.
 *
 * By default the body is parsed as JSON (falling back to URL-encoded parsing
 * when the `Content-Type` is `application/x-www-form-urlencoded`). Other body
 * types, such as `multipart/form-data`, must be opted into explicitly via
 * `options.type` and are never auto-detected from the request headers.
 *
 * @example
 * app.post("/", async (event) => {
 *   const body = await readBody(event);
 * });
 * @example
 * app.post("/upload", async (event) => {
 *   const body = await readBody(event, { type: "formData" });
 * });
 *
 * @param event H3 event passed by h3 handler
 * @param options Parsing options. Set `type` to force a parser instead of
 *   inferring it from the request `Content-Type`.
 *
 * @return {*} The `Object`, `Array`, `String`, `Number`, `Boolean`, or `null` value corresponding to the request body
 */
export declare function readBody<T, _Event extends HTTPEvent = HTTPEvent, _T = InferEventInput<"body", _Event, T>>(event: _Event, options?: ReadBodyOptions): Promise<undefined | _T>;
export declare function readValidatedBody<Event extends HTTPEvent, S extends StandardSchemaV1>(event: Event, validate: S, options?: ReadBodyOptions & {
  onError?: (result: FailureResult) => ErrorDetails;
}): Promise<InferOutput<S>>;
export declare function readValidatedBody<Event extends HTTPEvent, OutputT, InputT = InferEventInput<"body", Event, OutputT>>(event: Event, validate: (data: InputT) => ValidateResult<OutputT> | Promise<ValidateResult<OutputT>>, options?: ReadBodyOptions & {
  onError?: () => ErrorDetails;
}): Promise<OutputT>;
/**
 * Asserts that the request body size is within the specified limit.
 *
 * The limit is enforced **as the body is read**, not by pre-buffering: the
 * request is wrapped by srvx's `limitRequestBody`, which counts bytes as they
 * flow and aborts with a `413` {@link HTTPError} the moment the running total
 * exceeds `limit` (the error is injected via `createError`). This preserves the
 * byte-accurate guarantee (a lying-small `Content-Length` is still caught
 * mid-stream) without holding the body in memory or blocking streaming handlers.
 *
 * An honest `Content-Length` that already exceeds the limit is rejected up-front
 * with a `413`, and a request carrying both `Content-Length` and
 * `Transfer-Encoding` is rejected with a `400` (request smuggling, RFC 7230).
 *
 * Because enforcement is tied to consumption, an overflow on a chunked /
 * unknown-length body surfaces when the handler reads the body rather than as a
 * pre-handler `413`, and a body the handler never reads is never counted.
 *
 * @example
 * app.post("/", async (event) => {
 *   assertBodySize(event, 10 * 1024 * 1024); // 10MB
 *   const data = await event.req.formData();
 * });
 *
 * @param event HTTP event
 * @param limit Body size limit in bytes
 */
export declare function assertBodySize(event: HTTPEvent, limit: number): void;
/**
 * Parse the request to get HTTP Cookie header string and returning an object of all cookie name-value pairs.
 * @param event {HTTPEvent} H3 event or req passed by h3 handler
 * @returns Object of cookie name-value pairs
 * ```ts
 * const cookies = parseCookies(event)
 * ```
 */
export declare function parseCookies(event: HTTPEvent): Record<string, string | undefined>;
/**
 * Get and validate all cookies using a Standard Schema or custom validator.
 *
 * @example
 * app.get("/", async (event) => {
 *   const cookies = await getValidatedCookies(event, z.object({
 *     session: z.string(),
 *     theme: z.enum(["light", "dark"]).optional(),
 *   }));
 * });
 */
export declare function getValidatedCookies<Event extends HTTPEvent, S extends StandardSchemaV1<any, any>>(event: Event, validate: S, options?: {
  onError?: (result: FailureResult) => ErrorDetails;
}): Promise<InferOutput<S>>;
export declare function getValidatedCookies<Event extends HTTPEvent, OutputT>(event: Event, validate: (data: Record<string, string | undefined>) => ValidateResult<OutputT> | Promise<ValidateResult<OutputT>>, options?: {
  onError?: () => ErrorDetails;
}): Promise<OutputT>;
/**
 * Get a cookie value by name.
 * @param event {HTTPEvent} H3 event or req passed by h3 handler
 * @param name Name of the cookie to get
 * @returns {*} Value of the cookie (String or undefined)
 * ```ts
 * const authorization = getCookie(request, 'Authorization')
 * ```
 */
export declare function getCookie(event: HTTPEvent, name: string): string | undefined;
/**
 * Set a cookie value by name.
 * @param event {H3Event} H3 event or res passed by h3 handler
 * @param name Name of the cookie to set
 * @param value Value of the cookie to set
 * @param options {CookieSerializeOptions} Options for serializing the cookie
 * ```ts
 * setCookie(res, 'Authorization', '1234567')
 * ```
 */
export declare function setCookie(event: H3Event, name: string, value: string, options?: CookieSerializeOptions): void;
/**
 * Remove a cookie by name.
 * @param event {H3Event} H3 event or res passed by h3 handler
 * @param name Name of the cookie to delete
 * @param serializeOptions {CookieSerializeOptions} Cookie options
 * ```ts
 * deleteCookie(res, 'SessionId')
 * ```
 */
export declare function deleteCookie(event: H3Event, name: string, serializeOptions?: CookieSerializeOptions): void;
/**
 * Get a chunked cookie value by name. Will join chunks together.
 * @param event {HTTPEvent} { req: Request }
 * @param name Name of the cookie to get
 * @returns {*} Value of the cookie (String or undefined)
 * ```ts
 * const session = getChunkedCookie(event, 'Session')
 * ```
 */
export declare function getChunkedCookie(event: HTTPEvent, name: string): string | undefined;
/**
 * Set a cookie value by name. Chunked cookies will be created as needed.
 * @param event {H3Event} H3 event or res passed by h3 handler
 * @param name Name of the cookie to set
 * @param value Value of the cookie to set
 * @param options {CookieSerializeOptions} Options for serializing the cookie
 * ```ts
 * setCookie(res, 'Session', '<session data>')
 * ```
 */
export declare function setChunkedCookie(event: H3Event, name: string, value: string, options?: CookieSerializeOptions & {
  chunkMaxLength?: number;
}): void;
/**
 * Remove a set of chunked cookies by name.
 * @param event {H3Event} H3 event or res passed by h3 handler
 * @param name Name of the cookie to delete
 * @param serializeOptions {CookieSerializeOptions} Cookie options
 * ```ts
 * deleteCookie(res, 'Session')
 * ```
 */
export declare function deleteChunkedCookie(event: H3Event, name: string, serializeOptions?: CookieSerializeOptions): void;
/**
 * Options for the {@link EventStream} constructor.
 *
 * Currently empty — reserved for future configuration.
 */
export interface EventStreamOptions {}
/**
 * See https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#fields
 */
export interface EventStreamMessage {
  id?: string;
  event?: string;
  retry?: number;
  data: string;
}
/**
 * A helper class for [server sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format)
 *
 * Extends {@link HTTPResponse} so it can be returned directly from a handler
 * (`return eventStream`) — `toResponse` already renders any `HTTPResponse` as
 * the response, streaming the readable side with the SSE headers below.
 *
 * @example
 *
 * ```ts
 * import { EventStream } from "h3";
 *
 * app.get("/sse", (event) => {
 *   const eventStream = new EventStream(event);
 *
 *   // Send a message every second
 *   const interval = setInterval(async () => {
 *     await eventStream.push("Hello world");
 *   }, 1000);
 *
 *   // cleanup the interval when the connection is terminated
 *   eventStream.onClosed(() => clearInterval(interval));
 *
 *   return eventStream;
 * });
 * ```
 */
export declare class EventStream extends HTTPResponse {
  private readonly _event;
  private readonly _transformStream;
  private readonly _writer;
  private readonly _encoder;
  private readonly _closeCallbacks;
  private _writerIsClosed;
  private _paused;
  private _unsentData;
  private _disposed;
  private get _isClosed();
  constructor(event: H3Event, _opts?: EventStreamOptions);
  /**
   * Publish new event(s) for the client
   */
  push(message: string): Promise<void>;
  push(message: string[]): Promise<void>;
  push(message: EventStreamMessage): Promise<void>;
  push(message: EventStreamMessage[]): Promise<void>;
  pushComment(comment: string): Promise<void>;
  private _sendEvent;
  private _sendEvents;
  pause(): void;
  get isPaused(): boolean;
  resume(): Promise<void>;
  flush(): Promise<void>;
  /**
   * Close the stream and the connection if the stream is being sent to the client
   */
  close(): Promise<void>;
  /**
   * Triggers callback when the stream is closed, either by calling the
   * `close()` method or when the client disconnects.
   */
  onClosed(cb: () => any): void;
  /**
   * Return the readable side of the stream, staging the SSE headers on the event.
   *
   * @deprecated Return the stream itself instead (`return eventStream`) — it
   * carries the same headers via {@link HTTPResponse}. Kept for compatibility
   * with the `return eventStream.send()` pattern.
   */
  send(): Promise<BodyInit>;
}
/**
 * Append a `Server-Timing` entry to the response.
 *
 * Multiple calls append to the same header (comma-separated per spec).
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing
 *
 * @example
 * app.get("/", (event) => {
 *   setServerTiming(event, "db", { dur: 53, desc: "Database query" });
 *   return { data: "..." };
 * });
 * // Response header: Server-Timing: db;desc="Database query";dur=53
 */
export declare function setServerTiming(event: H3Event, name: string, opts?: {
  dur?: number;
  desc?: string;
}): void;
/**
 * Measure an async operation and append the timing to the `Server-Timing` header.
 *
 * Uses `performance.now()` for high-resolution timing.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing
 *
 * @example
 * app.get("/", async (event) => {
 *   const users = await withServerTiming(event, "db", () => fetchUsers());
 *   return users;
 * });
 * // Response header: Server-Timing: db;dur=42.5
 */
export declare function withServerTiming<T>(event: H3Event, name: string, fn: () => T | Promise<T>): Promise<T>;
/**
 * Make sure the status message is safe to use in a response.
 *
 * Allowed characters: horizontal tabs, spaces or visible ascii characters: https://www.rfc-editor.org/rfc/rfc7230#section-3.1.2
 */
export declare function sanitizeStatusMessage(statusMessage?: string): string;
/**
 * Make sure the status code is a valid HTTP status code.
 */
export declare function sanitizeStatusCode(statusCode?: string | number, defaultStatusCode?: number): number;
export interface CacheConditions {
  modifiedTime?: string | Date;
  maxAge?: number;
  etag?: string;
  cacheControls?: string[];
  /**
   * `If-None-Match` to evaluate instead of the request header. A cache layer
   * that narrows the request it forwards holds the only copy of the validator.
   */
  ifNoneMatch?: string;
  /** `If-Modified-Since` to evaluate instead of the request header. */
  ifModifiedSince?: string;
}
/**
 * Check request caching headers (`If-None-Match`, `If-Modified-Since`) and add caching headers (Last-Modified, ETag, Cache-Control).
 *
 * Note: `public` is added by default, but never alongside a caller-supplied `private`/`no-store` directive, so passing `cacheControls: ["private"]` no longer produces a contradictory `public, private`.
 * @returns `true` when cache headers are matching. When `true` is returned, no response should be sent anymore
 */
export declare function handleCacheHeaders(event: H3Event, opts: CacheConditions): boolean;
export interface ResolveDotSegmentsOptions {
  /**
   * Also decode percent-encoded path separators (`%2f`, `%5c`) into real `/`
   * segment boundaries before resolving `.`/`..`.
   *
   * `event.url.pathname` never decodes `%2f`, because doing so would change how
   * many segments a path has and therefore which route matches — a correctness
   * concern for dispatch, not just a security one
   * (e.g. `/files/:id` may rely on `%2F` to keep an id with a literal slash
   * as one opaque segment). So never use the result for routing/dispatch.
   *
   * Enable this for any out-of-band scope/security check whose result is
   * later handed to something that collapses `%2f` back to `/` on its own —
   * which is the common case, not an exotic one: an ordinary reverse proxy
   * (e.g. nginx with a trailing-slash `proxy_pass`) decodes `%2f`→`/` on every
   * request, so an encoded separator that dodges a narrower rule at match time
   * then escapes it downstream. If a scope check feeds a proxy or redirect
   * target, you almost certainly want this on.
   *
   * Decoding is pessimistic but bounded: it collapses a separator nested as
   * repeated whole `%25` prefixes (`%252f`, `%25252f`, ...) at any depth, so a
   * downstream that keeps `%25`-re-encoding and decoding cannot smuggle one
   * past. It does NOT catch a separator whose own hex digits are themselves
   * percent-encoded (`%25%32%66` → `%2f` → `/` after two decodes) — though that
   * exact spelling reaches a handler already canonicalized to `%252f`, which is
   * collapsed.
   * Treat this as covering the common `%25`-nesting case, not as an absolute
   * guarantee against every multi-decode chain. Other escapes (e.g. `%20`) are
   * never decoded.
   *
   * @default false
   */
  decodeSlashes?: boolean;
  /**
   * Collapse runs of consecutive path separators (interior empty `//` segments)
   * instead of preserving them, producing the *maximal-traversal* canonical
   * form — the path a slash-merging downstream (nginx `merge_slashes`, or any
   * backend that decodes then normalizes) actually resolves. It operates on the
   * separator set that is active after the normalizations above: a literal `/`,
   * a `\` normalized to `/`, and — with {@link decodeSlashes} — a decoded
   * `%2f`/`%5c` (so the same bounded `%25`-nesting boundary is inherited, and a
   * hex-of-hex form like `%25%32%66` is no more collapsed here than it is
   * decoded there).
   *
   * This is the reading in which a `..` next to an empty segment is no longer
   * shielded by it: `/a//..` resolves to `/`, not `/a`. The two readings diverge
   * exactly there, so a scope check that only looks at the empty-preserving form
   * can pass a path that still escapes downstream. Enable this for a fail-closed
   * scope/security check that must also hold against a slash-merging downstream
   * — but note a `/`-splitting router (rou3) does not merge slashes, so this
   * form is one of two readings such a check has to consider, not a replacement
   * for the other. Never use the result for routing/dispatch.
   *
   * Only *runs* collapse: a single trailing slash is preserved (`/a/` stays
   * `/a/`, `/a//` becomes `/a/`), as with nginx.
   *
   * @default false
   */
  mergeSlashes?: boolean;
}
/**
 * Resolve `.` and `..` segments in a path, without ever escaping above the
 * root `/`. The result is always an absolute path with a single leading `/`,
 * so it can never be protocol-relative (`//host`).
 *
 * Also decodes percent-encoded dot segments at any `%25`-nesting depth
 * (`%2e`, `%252e`, ...) and normalizes `\` to `/`, so encoded or
 * backslash-based traversal (e.g. `%2e%2e/`, `..\..\`) is caught the same
 * way as a literal `../`.
 *
 * `%2f`/`%5c` (encoded path separators) are left untouched by default — see
 * {@link ResolveDotSegmentsOptions.decodeSlashes}.
 *
 * Only `.`/`..` resolution and the decodes above alter the string; every other
 * percent-encoding (`%20`, non-ASCII, `%3A`, and any `%2e` not forming a whole
 * segment) is left intact, so the result stays in the same representation as
 * `event.url.pathname` and matches routes/rules consistently.
 * A trailing `.`/`..` resolves to a directory and keeps its trailing slash
 * (`/a/b/..` -> `/a/`, `/a/.` -> `/a/`), per RFC 3986 §5.2.4 and matching what a
 * WHATWG/nginx downstream resolves — so a scope check sees the directory form,
 * not its file-form sibling.
 * Interior empty segments are preserved (`/a//b` stays `/a//b`) — like WHATWG,
 * this never merges slashes, so empty segments survive rather than collapsing.
 * The one exception is a *leading* run: it is always clamped to a single `/`
 * (WHATWG would keep `//host`), so only the leading slash is guaranteed single
 * and a consumer doing exact prefix matching should normalize its allowlist the
 * same way. To collapse interior runs too (the reading a slash-merging
 * downstream resolves), see {@link ResolveDotSegmentsOptions.mergeSlashes}.
 */
export declare function resolveDotSegments(path: string, opts?: ResolveDotSegmentsOptions): string;
/**
 * Whether `path` is already canonical under `opts` — i.e. {@link resolveDotSegments}
 * would return it unchanged. Exact in both directions: `true` if and only if
 * `resolveDotSegments(path, opts) === path`.
 *
 * This is the resolver's own fast-path guard, exported so a caller that
 * canonicalizes on a hot path (per-request scope or rule matching) can skip the
 * call — and any work derived from it — without keeping its own copy of what the
 * resolver decodes. Such a copy goes stale silently, and a missed
 * canonicalization in a scope check is a bypass, not a perf bug.
 *
 * Pass the same options as the later {@link resolveDotSegments} call, or stricter
 * ones: `decodeSlashes`/`mergeSlashes` only add triggers, so `true` with both
 * enabled implies `true` in every mode. Checking one mode and resolving in
 * another voids the guarantee.
 *
 * Takes a bare pathname. Like the resolver, it has no notion of a query or hash
 * and scans one as if it were path, so `/a?next=/../b` is reported non-canonical
 * (and would resolve to `/b`).
 */
export declare function isCanonicalPath(path: string, opts?: ResolveDotSegmentsOptions): boolean;
/**
 * Normalize a route pattern into the canonical form h3 registers it under — the
 * same shape as the `event.url.pathname` it will be matched against.
 *
 * `app.on()`, `app.use(route, …)`, `app.mount()` and `removeRoute()` all apply
 * this to the pattern they receive. Use it when registering patterns into a
 * router of your own (e.g. a build-time compiled rou3 router) that is then
 * matched against h3's `event.url.pathname`, so both sides agree on the string —
 * a pattern that normalized differently could leave a route reachable while a
 * guard registered with the same source string matches nothing.
 *
 * A leading `/` is added if missing (`about` → `/about`), characters a request
 * pathname always carries percent-encoded are encoded (`/café/**` →
 * `/caf%C3%A9/**`), needless escapes are decoded the way h3 decodes them in the
 * request pathname (`/%40handle` → `/@handle`; `%2F` and `%25` stay encoded), and
 * `.`/`..` segments are resolved (`/a/b/../c` → `/a/c`). rou3 pattern syntax
 * (`?`, `{`, `}`, `^`, `\`) is left verbatim — spell one percent-encoded to match
 * it literally.
 *
 * Idempotent. Throws on an absolute URL (`http://…`): a route pattern is a
 * pathname, never a URL.
 *
 * @example
 * normalizeRoute("/について/**"); // "/%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/**"
 */
export declare function normalizeRoute(route: string): string;
export interface StaticAssetMeta {
  type?: string;
  etag?: string;
  mtime?: number | string | Date;
  size?: number;
  encoding?: string;
}
export interface ServeStaticOptions {
  /**
   * This function should resolve asset meta.
   *
   * **Security:** The `id` keeps encoded separators (`%2f`, `%5c`)
   * percent-encoded. Decoding them here re-introduces separators and defeats
   * the traversal normalization done by `serveStatic`. See {@link serveStatic}.
   */
  getMeta: (id: string) => StaticAssetMeta | undefined | Promise<StaticAssetMeta | undefined>;
  /**
   * This function should resolve asset content.
   *
   * **Security:** As with `getMeta`, the `id` must not be decoded before
   * resolving the asset. See {@link serveStatic}.
   */
  getContents: (id: string) => BodyInit | null | undefined | Promise<BodyInit | null | undefined>;
  /**
   * Headers to set on the response
   */
  headers?: HeadersInit;
  /**
   * Map of supported encodings (compressions) and their file extensions.
   *
   * Each extension will be appended to the asset path to find the compressed version of the asset.
   *
   * @example { gzip: ".gz", br: ".br" }
   */
  encodings?: Record<string, string>;
  /**
   * Default index file to serve when the path is a directory
   *
   * @default ["/index.html"]
   */
  indexNames?: string[];
  /**
   * When set to true, the function will not throw 404 error when the asset meta is not found or meta validation failed
   */
  fallthrough?: boolean;
  /**
   * Custom MIME type resolver function
   * @param ext - File extension including dot (e.g., ".css", ".js")
   */
  getType?: (ext: string) => string | undefined;
}
/**
 * Dynamically serve static assets based on the request path.
 *
 * **Security — path traversal:** `serveStatic` resolves `.`/`..` segments but
 * deliberately keeps encoded separators (`%2f`, `%5c`) percent-encoded in the
 * `id` it passes to `getMeta`/`getContents`, exactly as `event.url.pathname`
 * does. The `id` therefore has the same segment structure the router and
 * pathname-scoped `use()` guards matched on: `/private%5cx` stays one opaque
 * segment and cannot be served as `/private/x` past a `use("/private/**")`
 * guard. Resolve the `id` against your asset root as an opaque string — a
 * backend that decodes it re-introduces separators and re-opens the hole.
 *
 * A **non-canonical pathname is not served** (404, or falls through when
 * `fallthrough` is set): more than one leading separator (`//private/x`,
 * `/\\private/x`) or a dot segment that survived URL canonicalization, which
 * means one spelled with `%25`-nested escapes (`/pub/%252e%252e/private/x`).
 * Both dispatch to a catch-all route while missing a narrower
 * `use("/private/**")` guard, and the only `id` `serveStatic` could build from
 * them resolves back into the guarded path. Assets are reachable under their
 * canonical spelling — the one routing and `use()` guards match on — only.
 *
 * Everything else is decoded once for the on-disk lookup, so a file's real name
 * reaches the backend: `/50%25.png` → `/50%.png`, `/a%20b` → `/a b`, and one
 * `%25` level is peeled off a nested separator (`/a%252fb` → `/a%2fb`, still a
 * literal `%2f`, never a boundary). RFC 3986's reserved set stays encoded, so an
 * `id` can never grow a `?` or `#` that would truncate it in a URL.
 *
 * Two things `serveStatic` cannot enforce for filesystem-backed assets:
 * **case-insensitive filesystems** (macOS, Windows) need both sides of any
 * allow/deny check case-folded (otherwise `/SECRET.env` slips past a check for
 * `/secret.env`), and **symlinks** need the resolved path re-asserted against
 * the asset root after following links (e.g. `realpath(target)`).
 */
export declare function serveStatic(event: H3Event, options: ServeStaticOptions): Promise<HTTPResponse | undefined>;
/**
 * Returns a new event handler that removes the base url of the event before calling the original handler.
 *
 * @example
 * const api = new H3()
 *  .get("/", () => "Hello API!");
 * const app = new H3();
 *  .use("/api/**", withBase("/api", api.handler));
 *
 * @param base The base path to prefix.
 * @param handler The event handler to use with the adapted path.
 */
export declare function withBase<_RequestT extends EventHandlerRequest = EventHandlerRequest>(base: string, input: HTTPHandler<_RequestT>): EventHandler<ResolvedRequest<_RequestT>>;
type _BasicAuthOptions = {
  /**
   * Validate username for basic auth.
   */
  username: string;
  /***
   * Simple password for basic auth.
   */
  password: string;
  /**
   * Custom validation function for basic auth.
   *
   * When provided, the built-in non-empty check is skipped and this function
   * receives the decoded `username`/`password` as-is, including empty strings
   * (RFC 7617 permits an empty user-id and/or password). It must return `false`
   * to reject empty or otherwise invalid credentials.
   */
  validate: (username: string, password: string) => boolean | Promise<boolean>;
  /**
   * Realm for the basic auth challenge.
   *
   * Defaults to "auth".
   */
  realm: string;
};
export type BasicAuthOptions = Partial<_BasicAuthOptions> & ({
  validate: _BasicAuthOptions["validate"];
} | {
  password: _BasicAuthOptions["password"];
});
/**
 * Apply basic authentication for current request.
 *
 * @example
 * import { defineHandler, requireBasicAuth } from "h3";
 * export default defineHandler(async (event) => {
 *   await requireBasicAuth(event, { password: "test" });
 *   return `Hello, ${event.context.basicAuth.username}!`;
 * });
 */
export declare function requireBasicAuth(event: HTTPEvent, opts: BasicAuthOptions): Promise<true>;
/**
 * Create a basic authentication middleware.
 *
 * @example
 * import { H3, serve, basicAuth } from "h3";
 * const auth = basicAuth({ password: "test" });
 * app.get("/", (event) => `Hello ${event.context.basicAuth?.username}!`, [auth]);
 * serve(app, { port: 3000 });
 */
export declare function basicAuth(opts: BasicAuthOptions): Middleware;
export interface RequestFingerprintOptions {
  /** @default SHA-256 */
  hash?: false | "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512";
  /** @default `true` */
  ip?: boolean;
  /** @default `false` */
  xForwardedFor?: boolean;
  /** @default `false` */
  method?: boolean;
  /** @default `false` */
  url?: boolean;
  /** @default `false` */
  userAgent?: boolean;
}
/**
 *
 * Get a unique fingerprint for the incoming request.
 *
 * @experimental Behavior of this utility might change in the future versions
 */
export declare function getRequestFingerprint(event: HTTPEvent, opts?: RequestFingerprintOptions): Promise<string | null>;
/**
 * The `426 Upgrade Required` response returned by `defineWebSocketHandler()`
 * for WebSocket upgrade requests, with the resolved hooks attached as `crossws`.
 *
 * Convenience only: hooks are handed to adapters on the *request*
 * (`Symbol.for("crossws.hooks")`), because a `Response` is rebuilt whenever
 * anything stages a response header on the way out and a rebuild carries none of
 * the original's own properties. Read `crossws` off a response only when nothing
 * in the app can have touched it; `getWebSocketHooks(request)` from crossws is
 * the reliable read.
 *
 * `crossws` is always the resolved hooks object: when the handler is defined
 * with an async hooks factory, `defineWebSocketHandler()` awaits it before
 * attaching it.
 */
export type WebSocketResponse = Response & {
  crossws?: Partial<Hooks>;
};
/**
 * Define WebSocket hooks.
 *
 * @example
 * const hooks = defineWebSocket({
 *   open: (peer) => peer.send("Welcome!"),
 *   message: (peer, message) => peer.send(message.text()),
 *   close: (peer) => console.log("closed", peer),
 * });
 *
 * @see https://h3.dev/guide/websocket
 */
export declare function defineWebSocket(hooks: Partial<Hooks>): Partial<Hooks>;
export declare function defineWebSocketHandler(hooks: Partial<Hooks>): EventHandler<EventHandlerRequest, WebSocketResponse>;
export declare function defineWebSocketHandler(hooks: (event: H3Event) => Partial<Hooks> | Promise<Partial<Hooks>>): EventHandler<EventHandlerRequest, EventHandlerResponse<WebSocketResponse>>;
export declare function defineWebSocketHandler<Http extends EventHandler>(hooks: Partial<Hooks>, http: Http): EventHandler<EventHandlerRequest, WebSocketResponse | ReturnType<Http>>;
export declare function defineWebSocketHandler<Http extends EventHandler>(hooks: (event: H3Event) => Partial<Hooks> | Promise<Partial<Hooks>>, http: Http): EventHandler<EventHandlerRequest, EventHandlerResponse<WebSocketResponse> | ReturnType<Http>>;
/**
 * JSON-RPC 2.0 Interfaces based on the specification.
 * https://www.jsonrpc.org/specification
 */
/**
 * JSON-RPC 2.0 params.
 */
export type JsonRpcParams = Record<string, unknown> | unknown[];
/**
 * JSON-RPC 2.0 Request object.
 */
export interface JsonRpcRequest<I extends JsonRpcParams | undefined = JsonRpcParams | undefined> {
  jsonrpc: "2.0";
  method: string;
  params?: I;
  id?: string | number | null;
}
/**
 * JSON-RPC 2.0 Error object.
 */
export interface JsonRpcError {
  code: number;
  message: string;
  data?: any;
}
/**
 * JSON-RPC 2.0 Response object.
 */
export type JsonRpcResponse<O = unknown> = {
  jsonrpc: "2.0";
  id: string | number | null;
  result: O;
} | {
  jsonrpc: "2.0";
  id: string | number | null;
  error: JsonRpcError;
};
/**
 * A function that handles a JSON-RPC method call.
 * It receives the parameters from the request and the original H3Event.
 */
export type JsonRpcMethod<O = unknown, I extends JsonRpcParams | undefined = JsonRpcParams | undefined> = (data: JsonRpcRequest<I>, event: H3Event) => O | Promise<O>;
/**
 * A function that handles a JSON-RPC method call over WebSocket.
 * It receives the parameters from the request and the WebSocket peer.
 */
export type JsonRpcWebSocketMethod<O = unknown, I extends JsonRpcParams | undefined = JsonRpcParams | undefined> = (data: JsonRpcRequest<I>, peer: Peer) => O | Promise<O>;
/**
 * Creates an H3 event handler that implements the JSON-RPC 2.0 specification.
 *
 * **Security defaults:** requests must have a JSON `Content-Type` (CSRF, see
 * `validateContentType`), cross-origin requests are rejected (CSRF and DNS
 * rebinding, see `allowedOrigins`), and batches are capped at 50 requests
 * (fan-out amplification, see `maxBatchSize`).
 *
 * @param methods A map of RPC method names to their handler functions.
 * @param middleware Optional middleware to apply to the handler.
 * @returns An H3 EventHandler.
 *
 * @example
 * app.post(
 *   "/rpc",
 *   defineJsonRpcHandler({
 *     methods: {
 *       echo: ({ params }, event) => {
 *         return `Received \`${params}\` on path \`${event.url.pathname}\``;
 *       },
 *       sum: ({ params }, event) => {
 *         return params.a + params.b;
 *       },
 *     },
 *   }),
 * );
 */
export declare function defineJsonRpcHandler<RequestT extends EventHandlerRequest = EventHandlerRequest>(opts?: Omit<EventHandlerObject<RequestT>, "handler" | "fetch"> & {
  methods: Record<string, JsonRpcMethod>;
  /**
   * Maximum number of requests allowed in a single batch.
   *
   * Every batch item is dispatched concurrently, so an unbounded batch turns
   * one HTTP request into an arbitrary number of method invocations
   * (per-request rate limiters and quotas count it once) and fans out to
   * upstreams and database pools. Batches larger than this are rejected with
   * an `Invalid Request` (`-32600`) error.
   *
   * Set to `Infinity` to disable the limit.
   *
   * @default 50
   */
  maxBatchSize?: number;
  /**
   * Require a JSON `Content-Type` (`application/json`, `application/json-rpc`
   * or any `+json` media type) and reject anything else with a `415`.
   *
   * This is a CSRF defense: without it, an HTML form (or a typeless `fetch`
   * body) from an attacker page qualifies as a CORS "simple request" and is
   * delivered with the victim's cookies without any preflight. Requiring a
   * JSON content type forces a preflight for cross-origin callers.
   *
   * @default true
   */
  validateContentType?: boolean;
  /**
   * Origins allowed to call this endpoint.
   *
   * By default only same-origin requests are accepted: a request carrying an
   * `Origin` header that does not match the request's own origin is rejected
   * with a `403`. Requests without an `Origin` header (CLI clients,
   * server-to-server, MCP stdio bridges) are always allowed.
   *
   * Pass an explicit allowlist to accept specific cross-origin callers, or
   * `"*"` to disable the check entirely. An allowlist **replaces** the
   * same-origin default rather than extending it, so include this endpoint's
   * own origin as well when browsers served from it call it too.
   *
   * **Behind a proxy:** the same-origin default compares against
   * `event.url.origin`, derived from the request's own protocol and `Host`.
   * A TLS-terminating proxy leaves that `http:` while the browser sends an
   * `https:` `Origin`, so same-origin requests are rejected. Start the server
   * with srvx `trustProxy` when a proxy you control rewrites `X-Forwarded-*`,
   * or pass an explicit allowlist.
   *
   * **Security:** the MCP Streamable HTTP transport requires servers to
   * validate `Origin` to prevent DNS-rebinding attacks. The same-origin
   * default does not stop rebinding on its own (the rebound name is both the
   * `Origin` and the `Host`); locally bound servers should pass an explicit
   * allowlist of the origins they expect (e.g. `["http://localhost:3000"]`).
   *
   * Regular expressions are tested **unanchored** — always anchor them
   * (`/^https:\/\/app\.example\.com$/`).
   */
  allowedOrigins?: "*" | string | (string | RegExp)[] | ((origin: string) => boolean);
}): EventHandler<RequestT>;
/**
 * Creates an H3 event handler that implements JSON-RPC 2.0 over WebSocket.
 *
 * This is an opt-in feature that allows JSON-RPC communication over WebSocket
 * connections for bi-directional messaging. Each incoming WebSocket text message
 * is processed as a JSON-RPC request, and responses are sent back to the peer.
 *
 * **Security:** unlike `defineJsonRpcHandler()`, this does not check the request
 * `Origin`. WebSocket upgrades are not subject to CORS, so a page on any origin
 * can open a connection carrying the visitor's cookies (cross-site WebSocket
 * hijacking). Validate `Origin` in the `upgrade` hook and throw a `Response` to
 * abort the connection.
 *
 * @param opts Options including methods map and optional WebSocket hooks.
 * @returns An H3 EventHandler that upgrades to a WebSocket connection.
 *
 * @example
 * app.get(
 *   "/rpc/ws",
 *   defineJsonRpcWebSocketHandler({
 *     methods: {
 *       echo: ({ params }) => {
 *         return `Received: ${Array.isArray(params) ? params[0] : params?.message}`;
 *       },
 *       sum: ({ params }) => {
 *         return params.a + params.b;
 *       },
 *     },
 *   }),
 * );
 *
 * @example
 * // With additional WebSocket hooks
 * app.get(
 *   "/rpc/ws",
 *   defineJsonRpcWebSocketHandler({
 *     methods: {
 *       greet: ({ params }) => `Hello, ${params.name}!`,
 *     },
 *     hooks: {
 *       open(peer) {
 *         console.log(`Peer connected: ${peer.id}`);
 *       },
 *       close(peer, details) {
 *         console.log(`Peer disconnected: ${peer.id}`, details);
 *       },
 *     },
 *   }),
 * );
 */
export declare function defineJsonRpcWebSocketHandler(opts: {
  methods: Record<string, JsonRpcWebSocketMethod>;
  /**
   * Maximum number of requests allowed in a single batch message.
   *
   * Batch items are dispatched concurrently, so an unbounded batch lets a
   * single message fan out to an arbitrary number of method invocations.
   * Larger batches are rejected with an `Invalid Request` (`-32600`) error.
   *
   * Set to `Infinity` to disable the limit.
   *
   * @default 50
   */
  maxBatchSize?: number;
  hooks?: Partial<Omit<Hooks, "message">>;
}): EventHandler;
/** @deprecated Use `HTTPError` */
export type H3Error = HTTPError;
/** @deprecated Use `HTTPError` */
export declare const H3Error: typeof HTTPError;
/** @deprecated Use new HTTPError() */
export declare function createError(message: number, details?: ErrorDetails): HTTPError;
/** @deprecated Use new HTTPError() */
export declare function createError(details: ErrorDetails): HTTPError;
/**
 * @deprecated Use `HTTPError.isError`
 */
export declare function isError(input: any): input is HTTPError;
/** @deprecated Please use `event.url` */
export declare const getRequestPath: (event: H3Event) => string;
/** @deprecated Please use `event.req.headers.get(name)` */
export declare function getRequestHeader(event: H3Event, name: string): string | undefined;
/** @deprecated Please use `event.req.headers.get(name)` */
export declare const getHeader: (event: H3Event, name: string) => string | undefined;
/** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
export declare function getRequestHeaders(event: H3Event): Record<string, string>;
/** @deprecated Please use `Object.fromEntries(event.req.headers.entries())` */
export declare const getHeaders: (event: H3Event) => Record<string, string>;
/** @deprecated Please use `event.req.method` */
export declare function getMethod(event: H3Event, defaultMethod?: string): string;
/** @deprecated Please use `event.req.text()` or `event.req.arrayBuffer()` */
export declare function readRawBody<E extends "utf8" | false = "utf8">(event: H3Event, encoding?: E): E extends false ? Promise<Uint8Array | undefined> : Promise<string | undefined>;
/** @deprecated Please use `event.req.formData()` */
export declare function readFormDataBody(event: H3Event): Promise<FormData>;
/** @deprecated Please use `event.req.formData()` */
export declare const readFormData: (event: H3Event) => Promise<FormData>;
/** @deprecated Please use `event.req.formData()` */
export declare function readMultipartFormData(event: H3Event): Promise<Array<{
  data: Uint8Array;
  name?: string;
  filename?: string;
  type?: string;
}>>;
/** @deprecated Please use `event.req.body` */
export declare function getBodyStream(event: H3Event): ReadableStream<Uint8Array> | undefined;
/** @deprecated Please use `event.req.body` */
export declare const getRequestWebStream: (event: H3Event) => ReadableStream | undefined;
/** @deprecated Please directly return stream */
export declare function sendStream(_event: H3Event, value: ReadableStream): ReadableStream;
/** @deprecated Please use `return noContent(event)` */
export declare const sendNoContent: (event: H3Event, code?: number) => HTTPResponse;
/** @deprecated Please use `return redirect(event, code)` */
export declare const sendRedirect: (event: H3Event, location: string, code?: number) => HTTPResponse;
/** @deprecated Please directly return response */
export declare const sendWebResponse: (response: Response) => Response;
/** @deprecated Please use `return proxy(event)` */
export declare const sendProxy: (event: H3Event, target: string, opts?: ProxyOptions) => Promise<HTTPResponse>;
/** @deprecated Please use `new EventStream(event)` */
export declare function createEventStream(event: H3Event, opts?: EventStreamOptions): EventStream;
/** @deprecated Please use `return iterable(event, value)` */
export declare const sendIterable: <Value = unknown, Return = unknown>(_event: H3Event, val: IterationSource<Value, Return>, options?: {
  serializer: IteratorSerializer<Value | Return>;
}) => Promise<HTTPResponse>;
/** @deprecated Please use `event.res.statusText` */
export declare function getResponseStatusText(event: H3Event): string;
/** @deprecated Please use `event.res.headers.append(name, value)` */
export declare function appendResponseHeader(event: H3Event, name: string, value: string | string[]): void;
/** @deprecated Please use `event.res.headers.append(name, value)` */
export declare const appendHeader: (event: H3Event, name: string, value: string | string[]) => void;
/** @deprecated Please use `event.res.headers.set(name, value)` */
export declare function setResponseHeader(event: H3Event, name: string, value: string | string[]): void;
/** @deprecated Please use `event.res.headers.set(name, value)` */
export declare const setHeader: (event: H3Event, name: string, value: string | string[]) => void;
/** @deprecated Please use `event.res.headers.set(name, value)` */
export declare function setResponseHeaders(event: H3Event, headers: Record<string, string>): void;
/** @deprecated Please use `event.res.headers.set(name, value)` */
export declare const setHeaders: (event: H3Event, headers: Record<string, string>) => void;
/** @deprecated Please use `event.res.status` */
export declare function getResponseStatus(event: H3Event): number;
/** @deprecated Please directly set `event.res.status` and `event.res.statusText` */
export declare function setResponseStatus(event: H3Event, code?: number, text?: string): void;
/** @deprecated Please use `event.res.headers.set("content-type", type)` */
export declare function defaultContentType(event: H3Event, type?: string): void;
/** @deprecated Please use `Object.fromEntries(event.res.headers.entries())` */
export declare function getResponseHeaders(event: H3Event): Record<string, string>;
/** @deprecated Please use `event.res.headers.get(name)` */
export declare function getResponseHeader(event: H3Event, name: string): string | undefined;
/** @deprecated Please use `event.res.headers.delete(name)` instead. */
export declare function removeResponseHeader(event: H3Event, name: string): void;
/** @deprecated Please use `event.res.headers.append(name, value)` */
export declare function appendResponseHeaders(event: H3Event, headers: Record<string, string>): void;
/** @deprecated Please use `event.res.headers.append(name, value)` */
export declare const appendHeaders: (event: H3Event, headers: Record<string, string>) => void;
/** @deprecated Please use `event.res.headers.delete` */
export declare function clearResponseHeaders(event: H3Event, headerNames?: string[]): void;
export declare const defineEventHandler: typeof defineHandler;
export declare const eventHandler: typeof defineHandler;
export declare const lazyEventHandler: typeof defineLazyEventHandler;
/** @deprecated Please use `defineNodeHandler` */
export declare const defineNodeListener: typeof defineNodeHandler;
/** @deprecated Please use `defineNodeHandler` */
export declare const fromNodeMiddleware: (handler: NodeHandler | NodeMiddleware) => EventHandler;
/**
 * @deprecated please use `toNodeHandler` from `h3/node`.
 */
export declare function toNodeHandler(app: H3): NodeHandler;
/** @deprecated Please use `toNodeHandler` */
export declare const toNodeListener: (app: H3) => NodeHandler;
/** @deprecated Please use `new H3()` */
export declare const createApp: (config?: H3Config) => H3;
/** @deprecated Please use `new H3()` */
export declare const createRouter: (config?: H3Config) => H3;
/** @deprecated Please use `withBase()` */
export declare const useBase: (base: string, input: EventHandler | H3) => EventHandler;
export type { WebSocketHooks, WebSocketMessage, WebSocketPeer };