import { Readable } from 'stream';
import { Operation as JsonPatchOperation } from 'fast-json-patch';
import { MaybePromise } from '@httptoolkit/util';
import { Headers, Trailers, CompletedRequest, CompletedBody, Explainable, RawHeaders } from "../../types";
import { Replace } from '../../util/type-utils';
import { MatchReplacePairs, SerializedMatchReplacePairs } from '../match-replace';
import { Serializable, ClientServerChannel, SerializedProxyConfig } from "../../serialization/serialization";
import { SerializedBody } from '../../serialization/body-serialization';
import { ProxyConfig } from '../proxy-config';
import { CADefinition, ForwardingOptions, PassThroughStepConnectionOptions, PassThroughLookupOptions, PassThroughInitialTransforms } from '../passthrough-handling-definitions';
/**
 * The definition of a request rule step, which can be passed to Mockttp to define
 * a rule.
 *
 * Implementation of the step is not included in the definition classes, but
 * instead exists in an *Impl class defined separately and used internally.
 */
export interface RequestStepDefinition extends Explainable, Serializable {
    type: keyof typeof StepDefinitionLookup;
}
export type SerializedBuffer = {
    type: 'Buffer';
    data: number[];
};
/**
 * Can be returned from callbacks to override parts of a request.
 *
 * All fields are optional, and omitted values will default to the original
 * request value.
 */
export interface CallbackRequestResult {
    /**
     * A replacement HTTP method, capitalized.
     */
    method?: string;
    /**
     * The full URL to send the request to. If set, this will redirect
     * the request and automatically update the Host header accordingly,
     * unless you also provide a `headers` value that includes a Host
     * header, in which case that will take used as-is.
     */
    url?: string;
    /**
     * The replacement HTTP headers, as an object of string keys and either
     * single string or array of string values.
     */
    headers?: Headers;
    /**
     * A string or buffer, which replaces the request body if set. This will
     * be automatically content-encoded to match the Content-Encoding defined
     * in your request headers.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    body?: string | Buffer | Uint8Array;
    /**
     * A buffer, which replaces the request body if set, which is sent exactly
     * as is, and is not automatically encoded.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    rawBody?: Buffer | Uint8Array;
    /**
     * A JSON value, which will be stringified and send as a JSON-encoded
     * request body. This will be automatically content-encoded to match
     * the Content-Encoding defined in your request headers.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    json?: any;
    /**
     * A response: either a response object defining the fields of a response
     * or the string 'close' to immediately close the connection.
     *
     * See {@link CallbackResponseMessageResult} for the possible fields that can
     * be set to define the response.
     *
     * If set, the request will not be forwarded at all, and this will be used
     * as the response to immediately return to the client (or for 'close', this
     * will immediately close the connection to the client).
     */
    response?: CallbackResponseResult;
}
export type CallbackResponseResult = CallbackResponseMessageResult | 'close' | 'reset';
/**
 * Can be returned from callbacks to define parts of a response, or
 * override parts when given an existing repsonse.
 *
 * All fields are optional, and omitted values will default to the original
 * response value or a default value.
 */
export interface CallbackResponseMessageResult {
    /**
     * The response status code as a number.
     *
     * Defaults to 200 if not set.
     */
    statusCode?: number;
    /**
     * The response status message, as a string. This is ignored for
     * HTTP/2 responses.
     *
     * Defaults to the default status message for the status code if not set.
     */
    statusMessage?: string;
    /**
     * The replacement HTTP headers, as an object of string keys and either
     * single string or array of string values.
     *
     * Defaults to a minimum set of standard required headers if not set.
     */
    headers?: Headers;
    /**
     * The replacement HTTP trailers, as an object of string keys and either
     * single string or array of string values. Note that there are not all
     * header fields are valid as trailers, and there are other requirements
     * such as chunked encoding that must be met for trailers to be sent
     * successfully.
     */
    trailers?: Trailers;
    /**
     * A string or buffer, which replaces the response body if set. This will
     * be automatically encoded to match the Content-Encoding defined in your
     * response headers.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * Defaults to empty.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    body?: string | Buffer | Uint8Array;
    /**
     * A buffer, which replaces the response body if set, which is sent exactly
     * as is, and is not automatically encoded.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    rawBody?: Buffer | Uint8Array;
    /**
     * A JSON value, which will be stringified and send as a JSON-encoded
     * request body. This will be automatically content-encoded to match the
     * Content-Encoding defined in your response headers.
     *
     * If this is set, the Content-Length header will be automatically updated
     * or added accordingly, if required.
     *
     * You should only return one body field: either `body`, `rawBody` or
     * `json`.
     */
    json?: any;
}
export declare class FixedResponseStep extends Serializable implements RequestStepDefinition {
    status: number;
    statusMessage?: string | undefined;
    data?: (string | Uint8Array | Buffer | SerializedBuffer) | undefined;
    headers?: Headers | undefined;
    trailers?: Trailers | undefined;
    readonly type = "simple";
    static readonly isFinal = true;
    constructor(status: number, statusMessage?: string | undefined, data?: (string | Uint8Array | Buffer | SerializedBuffer) | undefined, headers?: Headers | undefined, trailers?: Trailers | undefined);
    explain(): string;
}
/**
 * @internal
 */
export interface SerializedCallbackStepData {
    type: string;
    name?: string;
}
/**
 * @internal
 */
export interface CallbackRequestMessage {
    args: [Replace<CompletedRequest, {
        body: SerializedBody;
    }>];
}
export declare class CallbackStep extends Serializable implements RequestStepDefinition {
    callback: (request: CompletedRequest) => MaybePromise<CallbackResponseResult>;
    readonly type = "callback";
    static readonly isFinal = true;
    constructor(callback: (request: CompletedRequest) => MaybePromise<CallbackResponseResult>);
    explain(): string;
    /**
     * @internal
     */
    serialize(channel: ClientServerChannel): SerializedCallbackStepData;
}
/**
 * @internal
 */
export interface SerializedStreamStepData {
    type: string;
    status: number;
    headers?: Headers;
}
export declare class StreamStep extends Serializable implements RequestStepDefinition {
    status: number;
    stream: Readable & {
        done?: true;
    };
    headers?: Headers | undefined;
    readonly type = "stream";
    static readonly isFinal = true;
    constructor(status: number, stream: Readable & {
        done?: true;
    }, headers?: Headers | undefined);
    explain(): string;
    /**
     * @internal
     */
    serialize(channel: ClientServerChannel): SerializedStreamStepData;
}
export declare class FileStep extends Serializable implements RequestStepDefinition {
    status: number;
    statusMessage: string | undefined;
    filePath: string;
    headers?: Headers | undefined;
    readonly type = "file";
    static readonly isFinal = true;
    constructor(status: number, statusMessage: string | undefined, filePath: string, headers?: Headers | undefined);
    explain(): string;
}
export interface PassThroughResponse {
    id: string;
    statusCode: number;
    statusMessage?: string;
    headers: Headers;
    rawHeaders: RawHeaders;
    body: CompletedBody;
}
export interface PassThroughStepOptions extends PassThroughStepConnectionOptions {
    /**
     * A set of data to automatically transform a request. This includes properties
     * to support many transformation common use cases.
     *
     * For advanced cases, a custom callback using beforeRequest can be used instead.
     * Using this field however where possible is typically simpler, more declarative,
     * and can be more performant. The two options are mutually exclusive: you cannot
     * use both transformRequest and a beforeRequest callback.
     *
     * Only one transformation for each target (e.g. method, headers & body) can be
     * specified. If more than one is specified then an error will be thrown when the
     * rule is registered.
     */
    transformRequest?: RequestTransform;
    /**
     * A set of data to automatically transform a response. This includes properties
     * to support many transformation common use cases.
     *
     * For advanced cases, a custom callback using beforeResponse can be used instead.
     * Using this field however where possible is typically simpler, more declarative,
     * and can be more performant. The two options are mutually exclusive: you cannot
     * use both transformResponse and a beforeResponse callback.
     *
     * Only one transformation for each target (status, headers & body) can be
     * specified. If more than one is specified then an error will be thrown when the
     * rule is registered.
     */
    transformResponse?: ResponseTransform;
    /**
     * A callback that will be passed the full request before it is passed through,
     * and which returns an object that defines how the the request content should
     * be transformed before it's passed to the upstream server.
     *
     * The callback can return an object to define how the request should be changed.
     * All fields on the object are optional, and returning undefined is equivalent
     * to returning an empty object (transforming nothing).
     *
     * See {@link CallbackRequestResult} for the possible fields that can be set.
     */
    beforeRequest?: (req: CompletedRequest) => MaybePromise<CallbackRequestResult | void> | void;
    /**
     * A callback that will be passed the full response before it is passed through,
     * and which returns a value that defines how the the response content should
     * be transformed before it's returned to the client. The callback is also passed
     * the request that was sent to the server (as a 2nd parameter) for reference.
     *
     * The callback can either return an object to define how the response should be
     * changed, or the strings 'close' or 'reset' to immediately close/reset the
     * underlying connection.
     *
     * All fields on the object are optional, and returning undefined is equivalent
     * to returning an empty object (transforming nothing).
     *
     * See {@link CallbackResponseMessageResult} for the possible fields that can be set.
     */
    beforeResponse?: (res: PassThroughResponse, req: CompletedRequest) => MaybePromise<CallbackResponseResult | void> | void;
}
export interface RequestTransform extends PassThroughInitialTransforms {
    /**
     * Override the request protocol. If replaceHost & matchReplaceHost are not specified
     * and the URL no explicitly specified port, this will automatically switch to the
     * appropriate port (e.g. from 80 to 443).
     */
    setProtocol?: 'http' | 'https';
    /**
     * A replacement HTTP method. Case insensitive.
     */
    replaceMethod?: string;
    /**
     * A headers object which will be merged with the real request headers to add or
     * replace values. Headers with undefined values will be removed.
     */
    updateHeaders?: Headers;
    /**
     * A headers object which will completely replace the real request headers.
     */
    replaceHeaders?: Headers;
    /**
     * A string or buffer that replaces the request body entirely.
     *
     * If this is specified, the upstream request will not wait for the original request
     * body, so this may make responses faster than they would be otherwise given large
     * request bodies or slow/streaming clients.
     */
    replaceBody?: string | Uint8Array | Buffer;
    /**
     * The path to a file, which will be used to replace the request body entirely. The
     * file will be re-read for each request, so the body will always reflect the latest
     * file contents.
     *
     * If this is specified, the upstream request will not wait for the original request
     * body, so this may make responses faster than they would be otherwise given large
     * request bodies or slow/streaming clients.
     */
    replaceBodyFromFile?: string;
    /**
     * A JSON object which will be merged with the real request body. Undefined values
     * will be removed, and other values will be merged directly with the target value
     * recursively.
     *
     * Any requests which are received with an invalid JSON body that match this rule
     * will fail.
     */
    updateJsonBody?: {
        [key: string]: any;
    };
    /**
     * A series of operations to apply to the request body in JSON Patch format (RFC
     * 6902).
     *
     * Any requests which are received with an invalid JSON body that match this rule
     * will fail.
     */
    patchJsonBody?: Array<JsonPatchOperation>;
    /**
     * Perform a series of string match & replace operations on the request body.
     */
    matchReplaceBody?: MatchReplacePairs;
}
export interface ResponseTransform {
    /**
     * A replacement response status code.
     */
    replaceStatus?: number;
    /**
     * A headers object which will be merged with the real response headers to add or
     * replace values. Headers with undefined values will be removed.
     */
    updateHeaders?: Headers;
    /**
     * A headers object which will completely replace the real response headers.
     */
    replaceHeaders?: Headers;
    /**
     * A string or buffer that replaces the response body entirely.
     *
     * If this is specified, the downstream response will not wait for the original response
     * body, so this may make responses arrive faster than they would be otherwise given large
     * response bodies or slow/streaming servers.
     */
    replaceBody?: string | Uint8Array | Buffer;
    /**
     * The path to a file, which will be used to replace the response body entirely. The
     * file will be re-read for each response, so the body will always reflect the latest
     * file contents.
     *
     * If this is specified, the downstream response will not wait for the original response
     * body, so this may make responses arrive faster than they would be otherwise given large
     * response bodies or slow/streaming servers.
     */
    replaceBodyFromFile?: string;
    /**
     * A JSON object which will be merged with the real response body. Undefined values
     * will be removed, and other values will be merged directly with the target value
     * recursively.
     *
     * Any responses which are received with an invalid JSON body that match this rule
     * will fail.
     */
    updateJsonBody?: {
        [key: string]: any;
    };
    /**
     * A series of operations to apply to the response body in JSON Patch format (RFC
     * 6902).
     *
     * Any responses which are received with an invalid JSON body that match this rule
     * will fail.
     */
    patchJsonBody?: Array<JsonPatchOperation>;
    /**
     * Perform a series of string match & replace operations on the response body.
     */
    matchReplaceBody?: MatchReplacePairs;
}
/**
 * @internal
 */
export interface SerializedPassThroughData {
    type: 'passthrough';
    forwarding?: ForwardingOptions;
    proxyConfig?: SerializedProxyConfig;
    ignoreHostCertificateErrors?: string[] | boolean;
    extraCACertificates?: Array<{
        cert: string;
    } | {
        certPath: string;
    }>;
    clientCertificateHostMap?: {
        [host: string]: {
            pfx: string;
            passphrase?: string;
        };
    };
    lookupOptions?: PassThroughLookupOptions;
    simulateConnectionErrors?: boolean;
    transformRequest?: Replace<RequestTransform, {
        'replaceBody'?: string;
        'updateHeaders'?: string;
        'updateJsonBody'?: string;
        'matchReplaceHost'?: {
            replacements: SerializedMatchReplacePairs;
            updateHostHeader?: boolean | string;
        };
        'matchReplacePath'?: SerializedMatchReplacePairs;
        'matchReplaceQuery'?: SerializedMatchReplacePairs;
        'matchReplaceBody'?: SerializedMatchReplacePairs;
    }>;
    transformResponse?: Replace<ResponseTransform, {
        'replaceBody'?: string;
        'updateHeaders'?: string;
        'updateJsonBody'?: string;
        'matchReplaceBody'?: SerializedMatchReplacePairs;
    }>;
    hasBeforeRequestCallback?: boolean;
    hasBeforeResponseCallback?: boolean;
}
/**
 * @internal
 */
export interface BeforePassthroughRequestRequest {
    args: [Replace<CompletedRequest, {
        body: SerializedBody;
    }>];
}
/**
 * @internal
 */
export interface BeforePassthroughResponseRequest {
    args: [
        Replace<PassThroughResponse, {
            body: SerializedBody;
        }>,
        Replace<CompletedRequest, {
            body: SerializedBody;
        }>
    ];
}
/**
 * Used in merging as a marker for values to omit, because lodash ignores undefineds.
 * @internal
 */
export declare const SERIALIZED_OMIT = "__mockttp__transform__omit__";
export declare class PassThroughStep extends Serializable implements RequestStepDefinition {
    readonly type = "passthrough";
    static readonly isFinal = true;
    readonly ignoreHostHttpsErrors: string[] | boolean;
    readonly clientCertificateHostMap: {
        [host: string]: {
            pfx: Buffer;
            passphrase?: string;
        };
    };
    readonly extraCACertificates: Array<CADefinition>;
    readonly transformRequest?: RequestTransform;
    readonly transformResponse?: ResponseTransform;
    readonly beforeRequest?: (req: CompletedRequest) => MaybePromise<CallbackRequestResult | void> | void;
    readonly beforeResponse?: (res: PassThroughResponse, req: CompletedRequest) => MaybePromise<CallbackResponseResult | void> | void;
    readonly proxyConfig?: ProxyConfig;
    readonly lookupOptions?: PassThroughLookupOptions;
    readonly simulateConnectionErrors: boolean;
    constructor(options?: PassThroughStepOptions);
    explain(): string;
    /**
     * @internal
     */
    serialize(channel: ClientServerChannel): SerializedPassThroughData;
}
export declare class CloseConnectionStep extends Serializable implements RequestStepDefinition {
    readonly type = "close-connection";
    static readonly isFinal = true;
    explain(): string;
}
export declare class ResetConnectionStep extends Serializable implements RequestStepDefinition {
    readonly type = "reset-connection";
    static readonly isFinal = true;
    explain(): string;
}
export declare class TimeoutStep extends Serializable implements RequestStepDefinition {
    readonly type = "timeout";
    static readonly isFinal = true;
    explain(): string;
}
export declare class JsonRpcResponseStep extends Serializable implements RequestStepDefinition {
    readonly result: {
        result: any;
        error?: undefined;
    } | {
        error: any;
        result?: undefined;
    };
    readonly type = "json-rpc-response";
    static readonly isFinal = true;
    constructor(result: {
        result: any;
        error?: undefined;
    } | {
        error: any;
        result?: undefined;
    });
    explain(): string;
}
export declare class DelayStep extends Serializable implements RequestStepDefinition {
    readonly delayMs: number;
    readonly type = "delay";
    static readonly isFinal = false;
    constructor(delayMs: number);
    explain(): string;
}
/**
 * A non-terminal step that sends an HTTP 1xx informational response
 * (e.g. 103 Early Hints) before any subsequent step. Multiple
 * informational responses may be sent before a final response.
 *
 * Status must be in 100-199, excluding 101.
 */
export declare class InformationalResponseStep extends Serializable implements RequestStepDefinition {
    readonly status: number;
    readonly headers?: (Headers | RawHeaders) | undefined;
    readonly type = "informational-response";
    static readonly isFinal = false;
    constructor(status: number, headers?: (Headers | RawHeaders) | undefined);
    explain(): string;
}
export declare class WaitForRequestBodyStep extends Serializable implements RequestStepDefinition {
    readonly type = "wait-for-request-body";
    static readonly isFinal = false;
    explain(): string;
}
export type RequestWebhookEvents = 'request' | 'response';
export declare class WebhookStep extends Serializable implements RequestStepDefinition {
    readonly url: string;
    readonly events: RequestWebhookEvents[];
    readonly type = "webhook";
    static readonly isFinal = false;
    constructor(url: string, events: RequestWebhookEvents[]);
    explain(): string;
}
export declare const StepDefinitionLookup: {
    simple: typeof FixedResponseStep;
    callback: typeof CallbackStep;
    stream: typeof StreamStep;
    file: typeof FileStep;
    passthrough: typeof PassThroughStep;
    'close-connection': typeof CloseConnectionStep;
    'reset-connection': typeof ResetConnectionStep;
    timeout: typeof TimeoutStep;
    'json-rpc-response': typeof JsonRpcResponseStep;
    delay: typeof DelayStep;
    'wait-for-request-body': typeof WaitForRequestBodyStep;
    webhook: typeof WebhookStep;
    'informational-response': typeof InformationalResponseStep;
};
//# sourceMappingURL=request-step-definitions.d.ts.map