export type RequestMetadata = {
    signal?: AbortSignal | null | undefined;
    /**
     * When calling a handler, if that handler is part of a handler chain, then by default an abort signal will be
     * sent to that handler if another handler higher up in the chain completes first. Usually this is desirable,
     * since we know if a handler higher up in the chain has completed and returned a response to its caller, then
     * the response from all the handlers below it in the chain will not be used.
     *
     * But in some cases, a handler in a handler chain wants to call the next handler in the chain as a
     * side-effect. If a handler is called as a side-effect, it will not be sent an abort signal when handlers
     * higher up in the chain complete.
     *
     * For example: a caching handler might return a cached value immediately, but then call the next handler as
     * a side-effect to fetch an updated value to refresh the cache in the background. In that case, the next
     * handler should not be sent an abort signal when the caching handler returns the cached value.
     */
    isSideEffect?: boolean;
} | undefined;
export type Handler<Req, Res, Meta extends RequestMetadata> = (req: Req, metadata?: Meta) => Promise<Res>;
export type ChainableHandler<Req, Res, NextReq, NextRes, Meta extends RequestMetadata | undefined> = (next: Handler<NextReq, NextRes, Meta>) => Handler<Req, Res, Meta>;
/**
 * Creates a Handler chain – a series of functions composed such that each function may call a supplied `next` function
 * which passes execution down the chain. When the final Handler in the chain returns, execution passes back up the
 * chain eventually returning to the caller.
 *
 * Each Handler chain begins with a "raw" Handler – this is a function which takes some request and returns some
 * response. A chain is then created by supplying a series of mapping functions – the ChainableHandler type – which will
 * be called with the `next` Handler in the chain.
 *
 * Ex:
 * ```ts
 * const handler = (request: string, metadata?: RequestMetadata) => Promise.resolve(`Responded to ${request}`)
 * const chainable = (next: Handler<string, string>) => (request: string, metadata?: RequestMetadata) => {
 *   return next(`modified ${request}`, metadata)
 * }
 *
 * const chain = new HandlerChainBuilder(handler)
 *   .map(chainable)
 *   .handler
 *
 * const response = await chain('hello')
 * expect(response).toBe('Responded to modified hello; 0')
 * ```
 * You can largely ignore the `metadata` argument present in the above example. This is the mechanism by which an
 * AbortSignal is passed to each Handler in the chain, but the only real requirement when implementing a Handler is
 * to pass this argument along to the `next` function. In fact, many Handlers will want to be generic over the type
 * of metadata:
 * ```ts
 * const chainable = <Meta>(next: Handler<string, string, Meta>) => (request: string, metadata: Meta) => {
 *   return next(`modified ${request}`, metadata)
 * }
 * ```
 * Actually, it's a very good idea for Handlers to be as generic as possible, since that will allow greater re-use. In
 * the above example, we don't do anything with the response from `next`, so we can let that be generic, too:
 * ```ts
 * const chainable = <Res, Meta>(next: Handler<string, Res, Meta>) => (request: string, metadata: Meta) => {
 *   return next(`modified ${request}`, metadata)
 * }
 * ```
 * Now if some other Handler in the chain decides to return a different response type, our Handler won't require any
 * changes to compile.
 *
 * ---
 *
 * Since execution passes from handler to handler in the chain, and then back, handlers have the opportunity to modify
 * or observe both the request and response. This might be useful for implementing serialization/deserialization, but
 * the simplest example that demonstrates this feature is measuring request latency:
 * ```ts
 * const latencyMeasuringHandler = <Req, Res, Meta>(next: Handler<Req, Res, Meta>) =>
 *   async (req: Req, metadata: Meta) => {
 *     const start = performance.now()
 *     const response = await next(req, metadata)
 *     const latency = performance.now() - start
 *     console.log(`latency for request ${request} was ${latency}`)
 *     return response
 *   }
 * ```
 * Execution is first passed to our measuring handler, which marks the `start` timestamp. Then it passes execution on
 * down the chain. After a response is received (by some handler down the chain), execution passes back up to our
 * handler here, which records the amount of time spent inside `next`.
 *
 * ---
 *
 * Handlers may also abort requests. They can do this in two ways:
 *   1. Create an `AbortController` and add its `AbortSignal` to the `metadata` object when calling `next`.
 *   2. Resolve its returned Promise.
 *
 * The first approach is straightforward, but the second may benefit from an example – the simplest is a handler which
 * will timeout a request:
 * ```ts
 * const timeoutHandler = <Req, Res, Meta>(next: Handler<Req, Res, Meta>) => (req: Req, metadata: Meta) => {
 *   return Promise.race([
 *     next(req, metadata),
 *     sleep(1000),
 *   ])
 * }
 * ```
 * The Promise returned by this handler will resolve either when the `next` handler resolves or 1 second has elapsed,
 * whichever happens first. If the timeout happens first, we want the `next` handler to recieve an abort signal so that
 * it can terminate early (since its result is no longer needed).
 *
 * HandlerChainBuilder makes this happen by observing when each handler completes, and sending an abort signal to all
 * the handlers "downstream" from the aborting handler.
 */
export declare class HandlerChainBuilder<Req, Res, Meta extends RequestMetadata> {
    private readonly inner;
    constructor(inner: (req: Req, metadata: Meta) => Promise<Res>);
    get handler(): Handler<Req, Res, Meta>;
    map<PriorReq, PriorRes>(outer: ChainableHandler<PriorReq, PriorRes, Req, Res, Meta>): HandlerChainBuilder<PriorReq, PriorRes, Meta>;
}
//# sourceMappingURL=HandlerChainBuilder.d.ts.map