import { DisposableSymbols } from '@whatwg-node/disposablestack';
import { MaybePromise } from '@whatwg-node/promise-helpers';
import { UnifiedGraphPlugin, Instrumentation as Instrumentation$2 } from '@graphql-mesh/fusion-runtime';
import { Plugin, YogaInitialContext, Instrumentation as Instrumentation$1 } from 'graphql-yoga';
import { MeshFetch, KeyValueCache, MeshFetchRequestInit, Logger as Logger$1 } from '@graphql-mesh/types';
import { FetchInstrumentation } from '@graphql-mesh/utils';
import { ExecutionRequest, MaybePromise as MaybePromise$1 } from '@graphql-tools/utils';
import { GraphQLResolveInfo } from 'graphql/type';
import { ServerAdapterInitialContext } from '@whatwg-node/server';

type MaybeLazy<T> = T | (() => T);
type AttributeValue = any;
type Attributes = AttributeValue[] | {
    [key: string | number]: AttributeValue;
};

interface LogWriter {
    write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void | Promise<void>;
    flush?(): void | Promise<void>;
}

type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
type RedactOptions = string[] | {
    paths: string[];
    censor?: string | ((value: unknown, path: string[]) => unknown) | undefined;
    remove?: boolean;
};
interface LoggerOptions {
    /**
     * The minimum log level to log.
     *
     * Providing `false` will disable all logging.
     *
     * Provided function will always be invoked to get the current log level.
     *
     * @default env.LOG_LEVEL || env.DEBUG ? 'debug' : 'info'
     */
    level?: MaybeLazy<LogLevel | false>;
    /** A prefix to include in every log's message. */
    prefix?: string;
    /**
     * The attributes to include in all logs. Is mainly used to pass the parent
     * attributes when creating {@link Logger.child child loggers}.
     */
    attrs?: Attributes;
    /**
     * The log writers to use when writing logs.
     *
     * @default env.LOG_JSON ? [new JSONLogWriter()] : [new ConsoleLogWriter()]
     */
    writers?: [LogWriter, ...LogWriter[]];
    /**
     * Paths that should have their values redacted from any log output.
     *
     * Can be an array of path strings or an object with `paths`, `censor`, and `remove` options.
     *
     * Note that using the `remove` option will set the value at the specified paths to `undefined`
     * instead of a deleting the key. This is the more performant option.
     *
     * @default undefined
     */
    redact?: RedactOptions;
}
declare class Logger implements AsyncDisposable {
    #private;
    constructor(opts?: LoggerOptions);
    /** The prefix that's prepended to each log message. */
    get prefix(): string | undefined;
    /**
     * The attributes that are added to each log. If the log itself contains
     * attributes with keys existing in {@link attrs}, the log's attributes will
     * override.
     */
    get attrs(): Attributes | undefined;
    /** The current {@link LogLevel} of the logger. You can change the level using the {@link setLevel} method. */
    get level(): false | LogLevel;
    /**
     * Sets the new {@link LogLevel} of the logger. All subsequent logs, and {@link child child loggers} whose
     * level did not change, will respect the new level.
     */
    setLevel(level: MaybeLazy<LogLevel | false>): void;
    write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void;
    flush(): Promise<void> | undefined;
    [DisposableSymbols.asyncDispose](): Promise<void | undefined>;
    child(prefix: string): Logger;
    child(attrs: Attributes, prefix?: string): Logger;
    log(level: LogLevel): void;
    log(level: LogLevel, attrs: MaybeLazy<Attributes>): void;
    log(level: LogLevel, msg: string, ...interpol: unknown[]): void;
    log(level: LogLevel, attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
    trace(): void;
    trace(attrs: MaybeLazy<Attributes>): void;
    trace(msg: string, ...interpol: unknown[]): void;
    trace(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
    debug(): void;
    debug(attrs: MaybeLazy<Attributes>): void;
    debug(msg: string, ...interpol: unknown[]): void;
    debug(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
    info(): void;
    info(attrs: MaybeLazy<Attributes>): void;
    info(msg: string, ...interpol: unknown[]): void;
    info(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
    warn(): void;
    warn(attrs: MaybeLazy<Attributes>): void;
    warn(msg: string, ...interpol: unknown[]): void;
    warn(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
    error(): void;
    error(attrs: MaybeLazy<Attributes>): void;
    error(msg: string, ...interpol: unknown[]): void;
    error(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
}

type TopicDataMap = {
    [topic: string]: any;
};
type PubSubListener<Data extends TopicDataMap, Topic extends keyof Data> = (data: Data[Topic]) => void;
interface PubSub<M extends TopicDataMap = TopicDataMap> {
    /**
     * Publish {@link data} for a {@link topic}.
     * @returns `void` or a `Promise` that resolves when the data has been successfully published
     */
    publish<Topic extends keyof M>(topic: Topic, data: M[Topic]): MaybePromise<void>;
    /**
     * A distinct list of all topics that are currently subscribed to.
     * Can be a promise to accomodate distributed systems where subscribers exist on other
     * locations and we need to know about all of them.
     */
    subscribedTopics(): MaybePromise<Iterable<keyof M>>;
    /**
     * Subscribe and listen to a {@link topic} receiving its data.
     *
     * If the {@link listener} is provided, it will be called whenever data is emitted for the {@link topic},
     *
     * @returns an unsubscribe function or a `Promise<unsubscribe function>` that resolves when the subscription is successfully established. the unsubscribe function returns `void` or a `Promise` that resolves on successful unsubscribe and subscription cleanup
     *
     * If the {@link listener} is not provided,
     *
     * @returns an `AsyncIterable` that yields data for the given {@link topic}
     */
    subscribe<Topic extends keyof M>(topic: Topic): AsyncIterable<M[Topic]>;
    subscribe<Topic extends keyof M>(topic: Topic, listener: PubSubListener<M, Topic>): MaybePromise<() => MaybePromise<void>>;
    /**
     * Closes active subscriptions and disposes of all resources. Publishing and subscribing after disposal
     * is not possible and will throw an error if attempted.
     */
    dispose(): MaybePromise<void>;
    /** @see {@link dispose} */
    [DisposableSymbols.asyncDispose](): Promise<void>;
}

interface GatewayConfigContext {
    /**
     * WHATWG compatible Fetch implementation.
     */
    fetch: MeshFetch;
    /**
     * The logger to use throught Hive and its plugins.
     */
    log: Logger;
    /**
     * Current working directory.
     * Note that working directory does not exist in serverless environments and will therefore be empty.
     */
    cwd: string;
    /**
     * Event bus for pub/sub.
     */
    pubsub?: PubSub;
    /**
     * Cache Storage
     */
    cache?: KeyValueCache;
}
interface GatewayContext extends GatewayConfigContext, YogaInitialContext {
    /**
     * Environment agnostic HTTP headers provided with the request.
     */
    headers: Record<string, string>;
    /**
     * Runtime context available within WebSocket connections.
     */
    connectionParams?: Record<string, string>;
}
type GatewayPlugin<TPluginContext extends Record<string, any> = Record<string, any>, TContext extends Record<string, any> = Record<string, any>> = Plugin<Partial<TPluginContext> & GatewayContext & TContext, GatewayConfigContext> & UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & {
    onFetch?: OnFetchHook<Partial<TPluginContext> & TContext>;
    onCacheGet?: OnCacheGetHook;
    onCacheSet?: OnCacheSetHook;
    onCacheDelete?: OnCacheDeleteHook;
    /**
     * An Instrumentation instance that will wrap each phases of the request pipeline.
     * This should be used primarily as an observability tool (for monitoring, tracing, etc...).
     *
     * Note: The wrapped functions in instrumentation should always be called. Use hooks to
     *       conditionally skip a phase.
     */
    instrumentation?: Instrumentation<TPluginContext & TContext & GatewayContext>;
};
interface OnFetchHookPayload<TContext> {
    url: string;
    setURL(url: URL | string): void;
    options: MeshFetchRequestInit;
    setOptions(options: MeshFetchRequestInit): void;
    /**
     * The context is not available in cases where "fetch" is done in
     * order to pull a supergraph or do some internal work.
     *
     * The logger will be available in all cases.
     */
    context: (GatewayContext & TContext) | {
        log: Logger;
    };
    /** @deprecated Please use `log` from the {@link context} instead. */
    logger: Logger$1;
    info: GraphQLResolveInfo;
    fetchFn: MeshFetch;
    setFetchFn: (fetchFn: MeshFetch) => void;
    executionRequest?: ExecutionRequest;
    endResponse: (response$: MaybePromise$1<Response>) => void;
}
interface OnFetchHookDonePayload {
    response: Response;
    setResponse: (response: Response) => void;
}
type OnFetchHookDone = (payload: OnFetchHookDonePayload) => MaybePromise$1<void>;
type OnFetchHook<TContext> = (payload: OnFetchHookPayload<TContext>) => MaybePromise$1<void | OnFetchHookDone>;
type OnCacheGetHook = (payload: OnCacheGetHookEventPayload) => MaybePromise$1<OnCacheGetHookResult | void>;
interface OnCacheGetHookEventPayload {
    cache: KeyValueCache;
    key: string;
    ttl?: number;
}
interface OnCacheGetHookResult {
    onCacheHit?: OnCacheHitHook;
    onCacheMiss?: OnCacheMissHook;
    onCacheGetError?: OnCacheErrorHook;
}
type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void;
interface OnCacheErrorHookPayload {
    error: Error;
}
type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void;
interface OnCacheHitHookEventPayload {
    value: any;
}
type OnCacheMissHook = () => void;
type OnCacheSetHook = (payload: OnCacheSetHookEventPayload) => MaybePromise$1<OnCacheSetHookResult | void>;
interface OnCacheSetHookResult {
    onCacheSetDone?: () => void;
    onCacheSetError?: OnCacheErrorHook;
}
interface OnCacheSetHookEventPayload {
    cache: KeyValueCache;
    key: string;
    value: any;
    ttl?: number;
}
type OnCacheDeleteHook = (payload: OnCacheDeleteHookEventPayload) => MaybePromise$1<OnCacheDeleteHookResult | void>;
interface OnCacheDeleteHookResult {
    onCacheDeleteDone?: () => void;
    onCacheDeleteError?: OnCacheErrorHook;
}
interface OnCacheDeleteHookEventPayload {
    cache: KeyValueCache;
    key: string;
}
type Instrumentation<TContext extends Record<string, any>> = Instrumentation$1<TContext> & Instrumentation$2 & FetchInstrumentation;

interface AWSSignv4PluginOptions {
    /**
     * Outgoing options for signing outgoing requests.
     */
    outgoing?: AWSSignv4PluginOutgoingOptions | AWSSignv4PluginOutgoingOptionsFactory;
    /**
     * Incoming options for validating incoming requests.
     */
    incoming?: AWSSignv4PluginIncomingOptions | boolean;
}
interface AWSSignv4PluginOutgoingOptions {
    /**
     * To sign the query instead of adding an Authorization header
     * @default false
     */
    signQuery?: boolean;
    /**
     * Service name to use when signing the request.
     * By default, it is inferred from the hostname.
     */
    serviceName?: string;
    /**
     * Region name to use when signing the request.
     * By default, it is inferred from the hostname.
     */
    region?: string;
    /**
     * ACCESS_KEY_ID
     * @default env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY
     */
    accessKeyId?: string;
    /**
     * AWS_SECRET_ACCESS_KEY
     * @default env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY
     */
    secretAccessKey?: string;
    /**
     * AWS_SESSION_TOKEN
     * @default env.AWS_SESSION_TOKEN
     */
    sessionToken?: string;
    /**
     * An identifier for the assumed role session.
     *
     * @default env.AWS_ROLE_ARN
     */
    roleArn?: string;
    /**
     * The Amazon Resource Names (ARNs) of the IAM managed policies that you want to use as
     * managed session policies. The policies must exist in the same account as the role.
     *
     * @default env.AWS_IAM_ROLE_SESSION_NAME
     */
    roleSessionName?: string;
}
interface AWSSignv4PluginOutgoingOptionsFactoryOptions {
    url: string;
    options: RequestInit;
    subgraphName: string;
}
type AWSSignv4PluginOutgoingOptionsFactory = (factoryOptions: AWSSignv4PluginOutgoingOptionsFactoryOptions) => MaybePromise<AWSSignv4PluginOutgoingOptions | undefined | false | true>;
interface AWSSignv4PluginIncomingPayload {
    /**
     * HTTP request
     */
    request: Request;
    /**
     * Context
     */
    serverContext: ServerAdapterInitialContext;
    /**
     * Incoming authorization headers string. Required.
     */
    authorization: string;
    /**
     * DateTime from incoming header. Required.
     */
    xAmzDate: string;
    /**
     * Additional header to set message exiration time even if signature message is valid. Optional.
     */
    xAmzExpires?: number;
    /**
     * Sha256 + formated hex for body. Empty body has own bodyHash. If there is no need to sign body for performance reason you can put UNSIGNED-PAYLOAD in request headers['x-amz-content-sha256'].
     */
    bodyHash: string;
    /**
     * accessKey used to sign this message.
     */
    accessKey: string;
    /**
     * Date used in authorization header.
     */
    date: string;
    /**
     * Region used in authorization header. Here can be any value.
     */
    region: string;
    /**
     * Service used in authorization header. Here can be any value.
     */
    service: string;
    /**
     * For aws4 will be aws4_request. Here can be any value.
     */
    requestType: string;
    /**
     * List of signed headers separated with semicolon.
     */
    signedHeaders: string;
    /**
     * Formated encoded header paris.
     */
    canonicalHeaders: string;
    secretAccessKey?: string;
}
interface AssumeRolePayload {
    /**
     * Region name to use when signing the request.
     * By default, it is inferred from the hostname.
     */
    region?: string;
    /**
     * An identifier for the assumed role session.
     *
     * @default env.AWS_ROLE_ARN
     */
    roleArn?: string;
    /**
     * The Amazon Resource Names (ARNs) of the IAM managed policies that you want to use as
     * managed session policies. The policies must exist in the same account as the role.
     *
     * @default env.AWS_IAM_ROLE_SESSION_NAME
     */
    roleSessionName?: string;
}
interface AWSSignv4PluginIncomingOptions {
    /**
     * Callback for secretKey. You have to provide process to get proper secret or return undefined secret.
     * By default it uses `accessKey` to get secret from `env.AWS_SECRET_ACCESS_KEY` or `env.AWS_SECRET_KEY`.
     * Should return secretKey on incoming parameters - but if secret is missing which it will be normal case when someone want to guess - you should return undefined;
     */
    secretAccessKey?: (payload: AWSSignv4PluginIncomingPayload) => MaybePromise<string | undefined> | string | undefined;
    /**
     * An identifier for the assumed role session.
     *
     * @default env.AWS_ROLE_ARN
     */
    assumeRole?: (payload: AWSSignv4PluginIncomingPayload) => MaybePromise<AssumeRolePayload | undefined> | AssumeRolePayload | undefined;
    /**
     * Callback for changes in incoming headers before it goes through parse process. Help to more sophisticated changes to preserve proper headers.
     */
    headers?: (headers: Headers) => Headers;
    /**
     * You can skip aws signature validation. It is useful when you want to use it only for some requests.
     */
    enabled?: (request: Request, serverContext: ServerAdapterInitialContext) => MaybePromise<boolean>;
    /**
         * Callback on header missing. Validation stops here. Default value `onMissingHeaders: () => {
                throw new GraphQLError('Headers are missing for auth', {
                  extensions: {
                    http: {
                      status: 401,
                    }
                  }
                });
              },`
         */
    onMissingHeaders?: (request: Request, serverContext: ServerAdapterInitialContext) => MaybePromise<void>;
    /**
         * Custom response on signature mismatch. Validation stops here. Default value `onSignatureMismatch: () => {
                throw new GraphQLError('The signature does not match', {
                  extensions: {
                    http: {
                      status: 401,
                    }
                  }
                });
              },`
         */
    onSignatureMismatch?: (request: Request, serverContext: ServerAdapterInitialContext) => MaybePromise<void>;
    /**
         * Custom response on exired time signature. Validation stops here. Default value `onExpired: () => {
                throw new GraphQLError('Request is expired', {
                  extensions: {
                    http: {
                      status: 401,
                    }
                  }
                });
              },`
         */
    onExpired?: (request: Request, serverContext: ServerAdapterInitialContext) => MaybePromise<void>;
    /**
     * Custom callback before standard parser comes. On false validation stops here. Default value `onBeforeParse: () => true,`
     *
     * Should return true if you need to let parse request further.
     */
    onBeforeParse?: (request: Request, serverContext: ServerAdapterInitialContext) => MaybePromise<boolean>;
    /**
     * Custom callback after standard parser done. On false validation stops here. Default value `onAfterParse: () => true,`
     * Should return true if you need to let parse request further.
     */
    onAfterParse?: (payload: AWSSignv4PluginIncomingPayload) => MaybePromise<boolean>;
    /**
     * Last callback after when validation signature is done. You can even stop here process.
     */
    onSuccess?: (payload: AWSSignv4PluginIncomingPayload) => MaybePromise<void>;
}
declare enum AWSSignV4Headers {
    'Authorization' = "authorization",
    'XAmzDate' = "x-amz-date",
    'XAmzContentSha256' = "x-amz-content-sha256",
    'XAmzExpires' = "x-amz-expires"
}

declare function useAWSSigv4<TContext extends Record<string, any>>(opts: AWSSignv4PluginOptions): GatewayPlugin<TContext>;

export { AWSSignV4Headers, type AWSSignv4PluginIncomingOptions, type AWSSignv4PluginIncomingPayload, type AWSSignv4PluginOptions, type AWSSignv4PluginOutgoingOptions, type AWSSignv4PluginOutgoingOptionsFactory, type AWSSignv4PluginOutgoingOptionsFactoryOptions, type AssumeRolePayload, useAWSSigv4 };
