/**
 * hosting/httpHost — one HTTP host, parameterised by the JSON dialect it speaks.
 *
 * Everything hard about serving an agent over HTTP happens once, here: draining
 * on close, aborting when the caller hangs up, failing a handler that throws,
 * failing a handler that answers nothing, mapping refusal codes onto status
 * codes, and choosing between one JSON body and Server-Sent Events based on
 * what the caller asked for.
 *
 * Everything a deployment target gets to re-decide is an {@link HttpWire}: the
 * two paths, and the JSON body shapes — how a request names its input and its
 * session, and what a health probe, a completion, a failure, a streamed piece,
 * a pending question and a resolved artifact look like on the wire.
 *
 * ── Why this file exists ─────────────────────────────────────────────────────
 * `nodeHost` shipped first and hard-coded its own dialect: `{ input }` in,
 * `{ output }` out, `{ status: 'ok' }` on the health path. That was fine while
 * it was the only HTTP adapter and wrong the moment there was a second one. A
 * container runtime dictates its own body shape as surely as it dictates its
 * paths, and an adapter for it should be a CONFIGURATION of the HTTP work, not
 * a second copy of it — a copy is where the drain semantics of two adapters
 * silently diverge.
 *
 * Note what did NOT have to change for that: the ports. `AgentHost`,
 * `HostRequest` and `HostReply` say exactly what they said before. The gap was
 * in the first adapter, which had no seam, not in the port, which needed none.
 *
 * ── The second thing a deployment gets to re-decide: who owns the socket ─────
 * By default this file creates a server and listens on it. Pass `server` and it
 * attaches to yours instead — because a container is sometimes given exactly
 * one port, and an agent that privately owns the socket cannot share it with a
 * WebSocket upgrade or with routes that were there first. Attached, the host
 * answers its two paths, writes nothing on anyone else's, and `close()`
 * detaches and drains without closing a socket it never opened. That is the
 * whole difference; every other law on this page is the same either way.
 *
 * ── …and the same seam, inverted: `onUnhandled` ──────────────────────────────
 * `server` lends the host a socket somebody else owns. `onUnhandled` lends the
 * CALLER every path this host does not own on a socket the host owns. One port
 * either way; which side binds it is the only difference. The host still never
 * answers for the application — with this hook it no longer has to 404 for it
 * either. Refused alongside `server`, where unmatched paths are already the
 * caller's and a second answer would just race the first.
 *
 * ── The law under all of it ──────────────────────────────────────────────────
 * **Nothing in a request's lifecycle may ever be the process's failure.** Every
 * listener body on this page that computes is wrapped, because node calls them
 * from its own stack and a throw there is uncaught — the death of a container,
 * bought with one malformed request. Stated in full at `readJson`, which is
 * where the field found it.
 *
 * ── The third door: a conversation ───────────────────────────────────────────
 * `serveConversations(handler)` sits beside `serve(handler)` and takes upgrades
 * on `conversationPath`, because `HostRequest → HostReply` is one exchange and
 * some doors are not. Give a host a `conversationPath` and it declares
 * `'conversation'`; leave it out and `serveConversations` refuses by name.
 *
 * **Both doors share ONE socket.** That is not an optimisation, it is the
 * premise: the runtimes that need a conversation are the ones that hand a
 * container exactly one port, so a host whose two doors each bound their own
 * would fail with `EADDRINUSE` on the deployment it exists for. On a private
 * socket the server is created by whichever door opens first and closed by
 * whichever closes last; on a caller-owned one each door attaches and detaches
 * its own listener and neither touches the socket.
 *
 * ── The door guard, on both doors ────────────────────────────────────────────
 * Being able to reach the port is not permission: every browser inside the
 * network can be steered by any page it opens. So before any handler sees a
 * request, the request door and the conversation door ask one guard
 * (`doorGuard.ts`): a Host this door was not configured for (421), a browser
 * origin it does not allow (403), a state-changing request that does not say
 * it is JSON (415) — and, once a dialect has read it, a session id over the
 * bound (400). Judged on the headers alone — a refused body is never parsed,
 * only drained when small so its sender reads the answer — and never invited
 * with a `100 Continue` on a socket this host owns. Answered with the
 * refusal's own sentence, reported to `onRefusal` listeners. Probes (the
 * health GET, the HEAD probe) are never asked; routes handed to `onUnhandled`
 * are the caller's. A loopback bind with `allowedHosts` unset answers the
 * loopback names only.
 *
 * Pattern: Template method via configuration (Strategy on the wire format).
 * Everything HTTP lives here and in the wires; `types.ts` knows none of it.
 */
/// <reference types="node" />
import type { IncomingMessage, Server, ServerResponse } from 'node:http';
import type { ArtifactWireRequest, ArtifactWireResult } from './artifactWire.js';
import { type CrossSiteOptions } from './doorGuard.js';
import type { SessionWireRequest, SessionWireResult } from './sessionWire.js';
import type { HostSignInOptions } from './signin/types.js';
import type { AgentHost, ConversationHandler, ConversationHost, ConversationLimits, HostCapability, HostHandle, HostHandler, PendingAsk } from './types.js';
import { type ConversationHandshake, type HandshakeFacts } from './webSocketConversation.js';
export type { ConversationHandshake, HandshakeFacts };
/** Everything a {@link HttpWire} may read when pulling a request apart. */
export interface HttpRequestFacts {
    /** The parsed JSON body, or `{}` for an empty one. */
    readonly body: Readonly<Record<string, unknown>>;
    /** Request headers with lower-cased names — so a wire never has to guess casing. */
    readonly headers: Readonly<Record<string, string>>;
    /** The query string, already parsed. */
    readonly query: URLSearchParams;
}
/**
 * The JSON dialect one deployment target speaks.
 *
 * A wire is pure: it reads facts and returns values. It never touches the
 * socket, never decides a status code, and never knows whether the reply is
 * going out as one body or as a stream of frames — those are {@link httpHost}'s
 * job, identical for every wire, which is the entire point of separating them.
 */
/**
 * One Server-Sent Event: the name it is announced under, and the body that
 * rides with it.
 */
/**
 * Where a failure came from.
 *
 * `'refused'` is the handler CHOOSING to fail with words it picked for the
 * caller. `'threw'` is an exception this host caught — the message is the
 * author's note to their own logs, and may name a query, a path or a token.
 *
 * The distinction exists because a dialect that sanitises has to sanitise the
 * right one: replacing both silences deliberate refusals, and replacing
 * neither publishes stack-trace prose to whoever is on the other end.
 */
export type FailureOrigin = 'refused' | 'threw';
export interface StreamFrame {
    readonly event: string;
    readonly data: unknown;
}
/**
 * The frames ONE streaming response is made of.
 *
 * ── Why this exists ──────────────────────────────────────────────────────────
 * A wire's body methods answer "what do the fields say". This answers a
 * different question: "what SHAPE is a stream". This host's own dialect frames
 * one homogeneous `chunk` per piece and one terminal frame, and that is a real
 * shape — but it is not the only one. Several protocols in wide use frame a
 * stream as a LIFECYCLE instead: named events that open a response, announce
 * that output is beginning, carry deltas, close each part, and close the
 * response — every event referring to one response object by id, often
 * numbered.
 *
 * Those cannot be expressed as "a body shape per chunk", because one host
 * lifecycle point becomes SEVERAL frames, and because the frames share state
 * (an id minted once, a counter that only goes up). So a dialect that frames
 * this way supplies one of these PER RESPONSE and keeps that state in it.
 *
 * ── The default is one of these ──────────────────────────────────────────────
 * A wire that supplies none gets framing built from its own body methods —
 * `chunk`/`complete`/`error`, exactly as before. The incumbent shape is an
 * INSTANCE of this seam rather than a special case beside it, which is the
 * reason to believe the seam is in the right place.
 */
export interface StreamFraming {
    /**
     * Frames to write the moment the stream opens, before the handler runs.
     * Absent or empty means the stream announces itself with nothing, which is
     * this host's own behaviour.
     */
    open?(): readonly StreamFrame[];
    /** Frames for one piece of streamed output. */
    chunk(text: string): readonly StreamFrame[];
    /** Frames that close a response that completed. */
    complete(output: string): readonly StreamFrame[];
    /** Frames that close a response that failed. */
    failure(message: string, code?: string, origin?: FailureOrigin): readonly StreamFrame[];
    /**
     * Frames that close a response ending in one of the other terminals. Absent
     * means the single-frame default, so a framing written for text alone keeps
     * working when an artifact or a paused run comes back through it.
     */
    awaiting?(pending: PendingAsk): readonly StreamFrame[];
    artifact?(result: ArtifactWireResult): readonly StreamFrame[];
    sessions?(result: SessionWireResult): readonly StreamFrame[];
}
export interface HttpWire {
    /**
     * Pull the port's vocabulary out of one request. Anything the wire cannot
     * find is simply absent — a missing input is the empty string, and a missing
     * session id means "no session", never an error, because refusing a request
     * on the shape of its body is a policy decision that belongs above the
     * transport.
     */
    readRequest(facts: HttpRequestFacts): {
        readonly input: string;
        readonly sessionId?: string;
        /**
         * The end user this request is for, when this dialect has a place the
         * transport puts one (9.12.0). Lands on {@link HostRequest.userId}, whose
         * note says what it is worth and what it is not.
         *
         * Optional, and a dialect with no such place returns nothing — the honest
         * answer for a wire whose transport never carried a user. Deriving one from
         * the session id would be this file inventing an actor.
         */
        readonly userId?: string;
        /**
         * A person's answer to an outstanding question, when this request carries
         * one. Its presence is what makes a request a RESUME rather than a new
         * message, so a wire that never returns it can only ever start new turns.
         */
        readonly decision?: unknown;
        /**
         * An artifact operation this request carries instead of a message
         * (9.23.0). Read it with `readArtifactWireOp(facts.body)` — the one owner
         * of the `{ op: 'artifact-head' | 'artifact-get', ref }` grammar — rather
         * than re-deriving the op names per dialect. A dialect that never returns
         * it simply cannot serve artifact resolution; a dialect that DOES must
         * also implement {@link HttpWire.artifact}, or every resolved ref answers
         * with the named not-carried refusal.
         */
        readonly artifact?: ArtifactWireRequest;
        /**
         * A session-history operation this request carries instead of a message
         * (9.26.0). Read it with `readSessionWireOp(facts.body)` — the one owner
         * of the `{ op: 'session-list' | 'session-transcript', sessionId? }`
         * grammar. A dialect that returns it must also implement
         * {@link HttpWire.sessions}, or every resolved listing answers with the
         * named not-carried refusal.
         */
        readonly session?: SessionWireRequest;
        /**
         * Headers to put on THIS request's reply, whatever terminal it ends with
         * (9.10.0).
         *
         * It exists for one shape: a dialect that ISSUES the session it just read —
         * a `Set-Cookie` for a session the caller did not carry. The wire stays
         * pure either way; it returns a value and this file writes it, the same as
         * the body shapes beside it.
         *
         * They are merged over the reply's own `content-type` (or the SSE headers),
         * so a dialect cannot accidentally break the framing this host chose:
         * `content-type` set here is ignored.
         */
        readonly responseHeaders?: Readonly<Record<string, string>>;
    };
    /**
     * Did THIS caller ask for a stream?
     *
     * Absent, the answer is the HTTP one: an `Accept` of `text/event-stream`.
     * That is the right default and the wrong rule for dialects that carry the
     * choice in the body instead — where a client sets a field and never touches
     * `Accept`, and a host reading only the header answers one JSON body to a
     * caller waiting for events.
     *
     * Present, this is the whole answer: a dialect that says how its callers ask
     * is not second-guessed by the header. Read before any reply is framed, so it
     * decides `content-type` for the request.
     */
    wantsStream?(facts: HttpRequestFacts): boolean;
    /**
     * Framing for ONE streaming response — see {@link StreamFraming}.
     *
     * Called once per streaming request, so whatever the framing has to remember
     * across its frames (an id minted for this response, a sequence counter) is
     * per-response state and never shared between callers. Absent, this host
     * frames the stream with the wire's own body shapes.
     */
    stream?(facts: HttpRequestFacts): StreamFraming;
    /** Body for a health probe. `uptimeMs` is how long this host has been serving. */
    health(uptimeMs: number): unknown;
    /**
     * Body for a reply that completed.
     *
     * `facts` is the request that produced it, for dialects whose reply repeats
     * something the request said — a model name, a conversation id. Absent only
     * where there is no request to show: this host has none to give when a body
     * is built outside a request's own lifecycle.
     */
    output(output: string, facts?: HttpRequestFacts): unknown;
    /**
     * Body for a reply that failed. `code` is the refusal's stable code, when it
     * has one — and its PRESENCE is the signal that the message was authored by
     * this library rather than thrown by somebody's handler, which is what lets a
     * dialect decide what is safe to repeat to a caller.
     */
    failure(message: string, code?: string, facts?: HttpRequestFacts, origin?: FailureOrigin): unknown;
    /** Body for one streamed piece, when the caller asked for Server-Sent Events. */
    chunk(text: string): unknown;
    /**
     * Body for a reply that is WAITING on a person — the run paused, it is stored,
     * and a later request carrying a decision continues it.
     *
     * Optional so a wire written before this terminal existed keeps compiling and
     * keeps working. A host whose wire has no `awaiting` cannot describe the
     * question, so it reports the named refusal instead — the run is still stored
     * either way.
     */
    awaiting?(pending: PendingAsk): unknown;
    /**
     * Body for a RESOLVED artifact operation (9.23.0) — the metadata for a
     * `head`, metadata + payload for a `get`. Compose it with
     * `artifactWireBody(result)` (both shipped dialects do) so clients read one
     * shape; add dialect envelope fields beside it when the deployment's
     * contract demands them.
     *
     * Optional exactly as `awaiting` is: a wire without it keeps compiling, and
     * a resolved ref on such a wire is answered with the named
     * `ArtifactNotCarriedError` refusal instead of an improvised body.
     */
    artifact?(result: ArtifactWireResult): unknown;
    /**
     * Body for a RESOLVED session-history operation (9.26.0) — the caller's own
     * sessions for a list, one owned session's messages for a transcript.
     * Compose it with `sessionWireBody(result)` (both shipped dialects do) so
     * clients read one shape.
     *
     * Optional exactly as `artifact` is: a wire without it keeps compiling, and
     * a resolved listing on such a wire is answered with the named
     * `SessionsNotCarriedError` refusal instead of an improvised body.
     */
    sessions?(result: SessionWireResult): unknown;
    /**
     * Pull the port's vocabulary out of one conversation handshake — the session
     * this conversation claims, any header mapping this dialect performs, and the
     * subprotocol to echo.
     *
     * Optional, and absent means "raw headers, no session": there is no body in a
     * handshake, so a dialect that wants a session id has to name where it looks,
     * and guessing on its behalf would invent an affinity rule the deployment
     * never agreed to.
     */
    readConversation?(facts: HandshakeFacts): ConversationHandshake;
}
/**
 * Options for {@link httpHost}.
 *
 * The three cross-site fields — `allowedOrigins`, `allowedHosts`,
 * `requireJsonContentType` — come from {@link CrossSiteOptions} and are
 * enforced by this host's door guard on its request door and its conversation
 * door alike, before any handler sees a request. Their defaults are the safe
 * ones; see `doorGuard.ts` for what each costs.
 */
export interface HttpHostOptions extends CrossSiteOptions {
    /**
     * Which adapter this is. Every refusal names it, so a caller reading an error
     * learns which adapter said no rather than which file it came from.
     */
    readonly name: string;
    /** The JSON dialect this host speaks. */
    readonly wire: HttpWire;
    /**
     * Path that takes a request. **Required, deliberately.** A default here would
     * be inherited by every adapter built on this file, and a default that
     * silently matched one runtime's container contract is exactly how a vendor
     * leaks into a library that promises not to know about one.
     */
    readonly invokePath: string;
    /** Path that answers a health probe. Required, for the same reason. */
    readonly healthPath: string;
    /**
     * Port to bind. Default `8080`. Pass `0` for an ephemeral port.
     *
     * Refused together with {@link HttpHostOptions.server}: a server you own
     * already has an address, and a port here would name a socket this host does
     * not bind.
     */
    readonly port?: number;
    /**
     * Interface to bind. Default `'0.0.0.0'`. Refused together with `server`, for the same reason.
     *
     * Every interface means every name that resolves to one of them — which is
     * why a host serving with `allowedHosts` unset says so once at boot.
     */
    readonly hostname?: string;
    /** What this adapter claims beyond the baseline. Default `['streaming']`. */
    readonly capabilities?: readonly HostCapability[];
    /**
     * A `node:http` server **you** own. Given one, this host ATTACHES its two
     * routes to it instead of creating and listening on a server of its own.
     *
     * ── Why ──────────────────────────────────────────────────────────────────
     * Some runtimes hand a container exactly one port, and a container that must
     * also answer a WebSocket upgrade — or anything else — on that port cannot
     * use a host that privately owns the socket. Attaching costs nothing anyone
     * else was using: `node:http` calls EVERY `'request'` listener for every
     * request, so this host and your own routes share the port by taking turns.
     *
     * ── What changes, exactly ────────────────────────────────────────────────
     *  - **You own the socket.** `listen()` is yours, and so is closing it. The
     *    server must ALREADY be listening when `serve()` is called — a handle
     *    that promises `url` and `port` cannot honestly report an address that
     *    does not exist yet, so `serve()` refuses rather than guess one.
     *  - **The host never writes a 404.** A path it does not own is yours to
     *    answer, and answering it with a refusal from this host would be this
     *    host answering for your application. Note the consequence: a request no
     *    listener answers is not a 404, it HANGS — if this server has no other
     *    `'request'` listener, unmatched paths go unanswered until the socket
     *    times out. With no `server`, the 404 behaviour is unchanged.
     *  - **`close()` detaches and drains, and leaves your server listening.** It
     *    removes this host's listener, waits for the requests it is already
     *    serving, and touches nothing else — not your connections, not your
     *    socket.
     *  - It never writes to a response an earlier listener already answered.
     *  - **A framework in front of it may mean this host never sees a request at
     *    all.** Frameworks that install a catch-all handler answer everything
     *    that reaches them, and a request they answered is finished before this
     *    host's listener runs. Register the framework's own route for these two
     *    paths and delegate to the host from inside it — or let the host own the
     *    socket and put your routes on {@link HttpHostOptions.onUnhandled}.
     *
     * @example  One port, an agent and a WebSocket upgrade
     *   const server = createServer();
     *   server.on('upgrade', (req, socket, head) => acceptWebSocket(req, socket, head));
     *   await new Promise<void>((r) => server.listen(8080, '0.0.0.0', r));
     *   const handle = await httpHost({ ...wireOptions, server }).serve(handler);
     *   // …later: the host goes away, the socket and the upgrade stay.
     *   await handle.close();
     */
    readonly server?: Server;
    /**
     * Path that takes a conversation upgrade.
     *
     * **No default, for exactly the reason `invokePath` has none** — a default
     * here is inherited by every adapter built on this file, and one that
     * silently matched somebody's runtime contract is that runtime leaking into a
     * library that promises not to know about one.
     *
     * ABSENT is meaningful: the host does not declare `'conversation'` and
     * {@link HttpHost.serveConversations} refuses by name. Present, and the host
     * can carry conversations whether or not anybody serves them.
     */
    readonly conversationPath?: string;
    /**
     * What the conversation door caps. Whatever is left out is filled with this
     * file's defaults and then DECLARED, so `conversationLimits` on the host is
     * always what is actually enforced rather than what was passed in.
     *
     * An unset `maxFrameBytes` would mean an unbounded buffer somebody else
     * fills, which is a way to kill this process; an unset `maxPendingBytes` the
     * same, one layer up. `idleMs` has no default because this door does not idle
     * anything out — it reports the ceiling of whatever sits in front of it, and
     * inventing one would be reporting a fact nobody established.
     */
    readonly conversationLimits?: ConversationLimits;
    /**
     * Answer a request whose path this host does not own — **your** code, on this
     * host's socket, INSTEAD of this host's 404.
     *
     * ── The law it states ────────────────────────────────────────────────────
     * The host never answers for the application. With this hook it no longer has
     * to 404 for it either: an unowned path arrives exactly as it came off the
     * wire, and what happens next is yours — a route of your own, a file, your
     * own 404, or nothing at all.
     *
     * This is the inverse of {@link HttpHostOptions.server}. There, you own the
     * socket and lend the host two paths; here, the host owns the socket and
     * lends you everything else. Same single port, opposite direction, and the
     * one to reach for when the host is the only thing that needs to bind.
     *
     * ── What it never receives ───────────────────────────────────────────────
     * The paths this host OWNS: `invokePath`, `healthPath` and `conversationPath`
     * — including a wrong METHOD on one of them, which is still this host's
     * question to answer. A hook that could claim `POST /invoke` would be a
     * second door wearing the first one's name.
     *
     * Absent, nothing changes: an unmatched path gets the same 404 it always did,
     * byte for byte.
     *
     * ── Private-server mode only ─────────────────────────────────────────────
     * Passed together with a caller-owned {@link HttpHostOptions.server} it is
     * REFUSED at construction, by name. There, unmatched paths are already yours
     * — they fall through to your own `'request'` listeners untouched — so this
     * would be a second answer to one question, and which answer won would depend
     * on the order two listeners were registered in.
     *
     * ── Two costs, stated rather than discovered ─────────────────────────────
     *  - A throw here is THAT REQUEST's 500 and never the process's failure, the
     *    same as everything else in a request's lifecycle.
     *  - A hook that answers NOTHING leaves the request hanging until it times
     *    out — the same price, for the same reason, as the 404 a caller-owned
     *    server does not get.
     *
     * @example  A diagnostic route beside the agent, on one port
     *   httpHost({
     *     ...wireOptions,
     *     onUnhandled: (req, res) => {
     *       if (req.url === '/debug/trace') {
     *         res.writeHead(200, { 'content-type': 'application/json' });
     *         res.end(JSON.stringify(lastTrace));
     *         return;
     *       }
     *       res.writeHead(404, { 'content-type': 'application/json' });
     *       res.end('{"error":"no such route"}');
     *     },
     *   });
     */
    readonly onUnhandled?: (req: IncomingMessage, res: ServerResponse) => void;
    /**
     * Ceiling on a request body, in bytes. Default: **none**.
     *
     * ── Why the default is no ceiling ────────────────────────────────────────
     * Not because unbounded is right — it is not. A body is memory this process
     * pays for while somebody else fills it, and a host without a ceiling can be
     * stopped by one caller with a large POST. The default is absent because
     * every adapter built on this file inherited unbounded reads before this
     * option existed, and a number chosen here would silently start refusing
     * requests that deployments are serving today.
     *
     * **Set it.** A deployment that knows the largest body it legitimately
     * carries should say so; a request over the line is refused with
     * `ERR_REQUEST_TOO_LARGE` (413) and the read is abandoned at the byte that
     * crossed it, rather than buffered to the end and then judged.
     */
    readonly maxBodyBytes?: number;
    /**
     * Answer `HEAD` on the invoke path with 204, instead of the 404 an unowned
     * method gets. Default `false`.
     *
     * Some deployment contracts probe a door before using it — "is the agent
     * here?" — and a probe is not a turn: nothing is read, nothing is run, and
     * the body is empty by definition. Opt-in, because a host that answered it
     * unasked would change what every existing adapter says to a method it has
     * always declined.
     */
    readonly invokeHeadProbe?: boolean;
    /**
     * Carry a sign-in cookie. The transport strips the cookie from every header
     * bag it hands onward and passes its KEY instead (`HostRequest.signInKey`,
     * `HostConversation.signInKey`); the conversation door verifies every
     * handshake with `signIn.identity` before the 101, re-checks the sign-in
     * before each inbound frame, and closes the socket when it ends. Absent —
     * the default — nothing about either door changes, byte for byte.
     */
    readonly signIn?: HostSignInOptions;
}
/** A {@link HostHandle} that also says where it landed. */
export interface HttpHostHandle extends HostHandle {
    /**
     * Where it is actually listening, e.g. `http://127.0.0.1:53211`. With a
     * caller-owned {@link HttpHostOptions.server} this is that server's real
     * address — the host reports where it is answering, never where it bound,
     * because with your server it bound nothing.
     */
    readonly url: string;
    /** The port it actually bound — the real one, when you asked for `0`. */
    readonly port: number;
}
/**
 * {@link AgentHost} and {@link ConversationHost} narrowed to an HTTP handle.
 *
 * One object with two doors, because they share one socket. Whether the
 * conversation door is USABLE is `capabilities`' answer, not this type's: a
 * host built without a `conversationPath` still has the method and refuses by
 * name, which is a better error than a method that is missing at runtime on
 * some adapters and present on others.
 */
export interface HttpHost extends AgentHost, ConversationHost {
    serve(handler: HostHandler): Promise<HttpHostHandle>;
    serveConversations(handler: ConversationHandler): Promise<HttpHostHandle>;
}
/**
 * An HTTP host for one handler, speaking the dialect you hand it.
 *
 * @example  The same machinery, two dialects
 *   httpHost({ name: 'nodeHost', wire: jsonWire, invokePath: '/invoke', healthPath: '/health' });
 *   httpHost({ name: 'myRuntime', wire: myWire, invokePath: '/v1/run', healthPath: '/up' });
 */
export declare function httpHost(options: HttpHostOptions): HttpHost;
/**
 * Read a header case-insensitively from already-lower-cased facts.
 *
 * Exported because every wire that maps a header needs it and re-deriving it
 * per adapter is how one of them ends up matching only the exact casing the
 * author happened to test with.
 *
 * Takes anything carrying lower-cased headers, so a request wire and a
 * handshake wire read a header the same way rather than each growing their own.
 */
export declare function headerValue(facts: {
    readonly headers: Readonly<Record<string, string>>;
}, name: string, ...fallbacks: string[]): string | undefined;
//# sourceMappingURL=httpHost.d.ts.map