import type { ConstValueNode, DirectiveNode, FieldNode, GraphQLEnumValueConfig, GraphQLFieldConfig, GraphQLInputFieldConfig, GraphQLInputObjectTypeConfig, GraphQLInputType, GraphQLNamedType, GraphQLObjectTypeConfig, GraphQLSchema, ValueNode } from "graphql";
import * as graphql from "graphql";
import type { Deferred } from "./deferred.js";
import type { LayerPlan, LayerPlanReasonDefer, LayerPlanReasonListItem, LayerPlanReasonSubscription } from "./engine/LayerPlan.js";
import type { OperationPlan } from "./engine/OperationPlan.js";
import type { BaseGraphQLArguments, GrafastFieldConfig, GrafastInputFieldConfig } from "./interfaces.js";
import type { Step } from "./step.js";
/**
 * The parent object is used as the key in `GetValueStepId()`; for root level
 * fields it's possible that the parent will be null/undefined (in all other
 * cases it will be an object), so we need a value that can be the key in a
 * WeakMap to represent the root.
 */
export declare const ROOT_VALUE_OBJECT: any;
export declare function assertNullPrototype(object: Record<string, unknown>, description: string): void;
/**
 * Specifically allows for the `defaultValue` to be undefined, but otherwise
 * defers to {@link rawValueToValueNode}
 */
export declare function defaultValueToValueNode(type: GraphQLInputType, defaultValue: unknown): ConstValueNode | undefined;
export declare function isPromise<T>(t: T | Promise<T>): t is Promise<T>;
/**
 * Is "thenable".
 */
export declare function isPromiseLike<T>(t: T | PromiseLike<T>): t is PromiseLike<T>;
/**
 * Is a promise that can be externally resolved.
 */
export declare function isDeferred<T>(t: T | Promise<T> | Deferred<T>): t is Deferred<T>;
/**
 * Returns true if array1 and array2 have the same length, and every pair of
 * values within them pass the `comparator` check (which defaults to `===`).
 */
export declare function arraysMatch<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, comparator?: (val1: T, val2: T) => boolean): boolean;
/**
 * Returns true if map1 and map2 have the same keys, and every matching entry
 * value within them pass the `comparator` check (which defaults to `===`).
 */
export declare function mapsMatch<TKey, TVal>(map1: ReadonlyMap<TKey, TVal>, map2: ReadonlyMap<TKey, TVal>, comparator?: (k: TKey, val1: TVal | undefined, val2: TVal | undefined) => boolean): boolean;
/**
 * Returns true if record1 and record2 are equivalent, i.e. every value within
 * them pass the `comparator` check (which defaults to `===`).
 *
 * Currently keys are ignored (`record[key] = undefined` is treated the same as
 * `record[key]` being unset), but this may not always be the case.
 */
export declare function recordsMatch<TRecord extends {
    readonly [k: string | symbol | number]: any;
}>(record1: TRecord, record2: TRecord, comparator?: (k: keyof TRecord, val1: TRecord[typeof k], val2: TRecord[typeof k]) => boolean): boolean;
export declare function setsMatch(s1: ReadonlySet<string> | null, s2: ReadonlySet<string> | null): boolean;
export type ObjectTypeFields<TParentStep extends Step> = {
    [key: string]: GrafastFieldConfig<TParentStep, any, any>;
};
export type ObjectTypeSpec<TParentStep extends Step, TFields extends ObjectTypeFields<TParentStep>> = Omit<GraphQLObjectTypeConfig<any, Grafast.Context>, "fields"> & {
    fields: TFields | (() => TFields);
    assertStep?: TParentStep extends Step ? ((step: Step) => asserts step is TParentStep) | {
        new (...args: any[]): TParentStep;
    } : null;
    planType?: ($specifier: Step) => TParentStep;
};
/**
 * Saves us having to write `extensions: {grafast: {...}}` everywhere.
 */
export declare function objectSpec<TParentStep extends Step, TFields extends ObjectTypeFields<TParentStep>>(spec: ObjectTypeSpec<TParentStep, TFields>): GraphQLObjectTypeConfig<any, Grafast.Context>;
export type GrafastObjectType<TParentStep extends Step, TFields extends ObjectTypeFields<TParentStep>> = graphql.GraphQLObjectType<TParentStep extends Step<infer U> ? U : never> & {
    TParentStep: TParentStep;
    TFields: TFields;
};
/**
 * @remarks This is a mess because the first two generics need to be specified manually, but the latter one we want inferred.
 */
export declare function newObjectTypeBuilder<TParentStep extends Step>(assertStep: TParentStep extends Step ? ((step: Step) => asserts step is TParentStep) | {
    new (...args: any[]): TParentStep;
} : never): <TFields extends ObjectTypeFields<TParentStep>>(spec: ObjectTypeSpec<TParentStep, TFields>) => GrafastObjectType<TParentStep, TFields>;
/**
 * Saves us having to write `extensions: {grafast: {...}}` everywhere.
 */
export declare function objectFieldSpec<TSource extends Step, TArgs extends BaseGraphQLArguments = BaseGraphQLArguments, TResult extends Step = Step>(grafastSpec: GrafastFieldConfig<TSource, TArgs, TResult>, path: string): GraphQLFieldConfig<any, Grafast.Context, TArgs>;
/**
 * "Constrainted identity function" for field configs.
 *
 * @see {@link https://kentcdodds.com/blog/how-to-write-a-constrained-identity-function-in-typescript}
 */
export declare function newGrafastFieldConfigBuilder<TParentStep extends Step>(): <TArgs extends BaseGraphQLArguments = BaseGraphQLArguments, TFieldStep extends Step = Step>(config: GrafastFieldConfig<TParentStep, TArgs, TFieldStep>) => typeof config;
export type GrafastInputFieldConfigMap<TParent> = {
    [key: string]: GrafastInputFieldConfig<TParent, GraphQLInputType>;
};
export type InputObjectTypeSpec<TParent> = Omit<GraphQLInputObjectTypeConfig, "fields"> & {
    fields: GrafastInputFieldConfigMap<TParent> | (() => GrafastInputFieldConfigMap<TParent>);
};
export type GrafastInputObjectType<TParent> = graphql.GraphQLInputObjectType & {
    TParent: TParent;
};
export declare function newInputObjectTypeBuilder<TParent = any>(): (spec: InputObjectTypeSpec<TParent>) => GrafastInputObjectType<TParent>;
/**
 * Saves us having to write `extensions: {grafast: {...}}` everywhere.
 */
export declare function inputObjectFieldSpec<TParent>(grafastSpec: GrafastInputFieldConfig<TParent, GraphQLInputType>, path: string): GraphQLInputFieldConfig;
declare module "graphql" {
    interface GraphQLEnumType {
        [$$valueConfigByValue]?: Record<string, GraphQLEnumValueConfig>;
    }
}
declare const $$valueConfigByValue: unique symbol;
export declare function getEnumValueConfigs(enumType: graphql.GraphQLEnumType): {
    [outputValue: string]: GraphQLEnumValueConfig | undefined;
};
/**
 * This would be equivalent to `enumType._valueLookup.get(outputValue)` except
 * that's not a public API so we have to do a bit of heavy lifting here. Since
 * it is heavy lifting, we cache the result, but we don't know when enumType
 * will go away so we use a weakmap.
 */
export declare function getEnumValueConfig(enumType: graphql.GraphQLEnumType, outputValue: string): GraphQLEnumValueConfig | undefined;
/**
 * Ridiculously, this is faster than `new Array(length).fill(fill)`
 */
export declare function arrayOfLength(length: number, fill?: any): any[];
export declare const valueNodeToStaticValue: typeof graphql.valueFromAST;
export declare function findVariableNamesUsedInValueNode(valueNode: ValueNode, variableNames: Set<string>): void;
/**
 * Given a FieldNode, recursively walks and finds all the variable references,
 * returning a list of the (unique) variable names used.
 */
export declare function findVariableNamesUsed(operationPlan: OperationPlan, field: FieldNode): string[];
export declare function isTypePlanned(schema: GraphQLSchema, namedType: GraphQLNamedType): boolean;
/**
 * Returns `true` if the first argument depends on the second argument either
 * directly or indirectly (via a chain of dependencies).
 */
export declare function stepADependsOnStepB(stepA: Step, stepB: Step): boolean;
/**
 * Returns true if stepA is allowed to depend on stepB, false otherwise. (This
 * mostly relates to heirarchy.)
 */
export declare function stepAMayDependOnStepB($a: Step, $b: Step): boolean;
export declare function stepAShouldTryAndInlineIntoStepB($a: Step, $b: Step): boolean;
export declare function pathsFromAncestorToTargetLayerPlan(ancestor: LayerPlan, lp: LayerPlan): readonly LayerPlan[][];
export declare function layerPlanHeirarchyContains(lp: LayerPlan, targetLp: LayerPlan): boolean;
/**
 * For a regular GraphQL query with no `@stream`/`@defer`, the entire result is
 * calculated and then the output is generated and sent to the client at once.
 * Thus you can think of this as every plan is in the same "phase".
 *
 * However, if you introduce a `@stream`/`@defer` selection, then the steps
 * inside that selection should run _later_ than the steps in the parent
 * selection - they should run in two different phases. Similar is true for
 * subscriptions.
 *
 * When optimizing your plans, if you are not careful you may end up pushing
 * what should be later work into the earlier phase, resulting in the initial
 * payload being delayed whilst things that should have been deferred are being
 * calculated. Thus, you should generally check that two plans are in the same phase
 * before you try and merge them.
 *
 * This is not a strict rule, though, because sometimes it makes more sense to
 * push work into the parent phase because it would be faster overall to do
 * that work there, and would not significantly delay the initial payload's
 * execution time - for example it's unlikely that it would make sense to defer
 * selecting an additional boolean column from a database table even if the
 * operation indicates that's what you should do.
 *
 * As a step class author, it's your responsiblity to figure out the right
 * approach. Once you have, you can use this function to help you, should you
 * need it.
 */
export declare function stepsAreInSamePhase(ancestor: Step, descendent: Step): boolean;
export declare function isPhaseTransitionLayerPlan(layerPlan: LayerPlan): layerPlan is LayerPlan<LayerPlanReasonListItem> | LayerPlan<LayerPlanReasonDefer> | LayerPlan<LayerPlanReasonSubscription>;
export declare const canonicalJSONStringify: (o: object) => string;
export declare function assertNotAsync(fn: any, name: string): void;
export declare function assertNotPromise<TVal>(value: TVal, fn: any, name: string): TVal;
export declare function hasItemPlan(step: Step & {
    itemPlan?: ($item: Step) => Step;
}): step is Step & {
    itemPlan: ($item: Step) => Step;
};
export declare function exportNameHint(obj: any, nameHint: string): void;
export declare function isTuple<T extends readonly [...(readonly any[])]>(t: any | T): t is T;
/**
 * Turns an array of keys into a digest, avoiding conflicts.
 * Symbols are treated as equivalent. (Theoretically faster
 * than JSON.stringify().)
 */
export declare function digestKeys(keys: ReadonlyArray<string | number | symbol>): string;
/**
 * If the directive has the argument `argName`, return a step representing that
 * arguments value, whether that be a step representing the relevant variable
 * or a constant step representing the hardcoded value in the document.
 *
 * @remarks NOT SUITABLE FOR USAGE WITH LISTS OR OBJECTS! Does not evaluate
 * internal variable usages e.g. `[1, $b, 3]`
 */
export declare function directiveArgument<T>(operationPlan: OperationPlan, directive: DirectiveNode, argName: string, expectedKind: graphql.Kind.INT | graphql.Kind.FLOAT | graphql.Kind.BOOLEAN | graphql.Kind.STRING): Step<T> | undefined;
export {};
//# sourceMappingURL=utils.d.ts.map