import type { BaseGraphQLArguments, ExecutionDetails, ExecutionDetailsStream, FieldArgs, GrafastResultsList, Maybe, PromiseOrDirect, StepOptimizeOptions, UnbatchedExecutionExtra } from "../interfaces.ts";
import { Step, UnbatchedStep } from "../step.ts";
import { ConstantStep } from "./constant.ts";
/**
 * Indicates which features are supported for pagination; support for `limit`
 * is assumed even in the case of an empty object. Features unsupported can be
 * emulated by the `connection()` step (but this is less efficient than
 * handling it yourself).
 */
export interface PaginationFeatures {
    /**
     * If you want to support reverse pagination, supporting `reverse` is vital.
     * Without it, we must fetch the entire collection (no limit, offset, or
     * cursor) in order to access the last entries.
     *
     * If you support `reverse` you MUST also support `cursor`, otherwise a
     * request such as `last: 3` cannot determine the cursors to use (since we
     * would need to know the length of the collection), and since cursors must
     * be the same values whether paginating forwards or backwards. If you cannot
     * support cursor, then simply do not indicate that you support `reverse` and
     * `connection()` will take care of pagination for you.
     *
     * To support `reverse`, you need to be able to apply the other supported
     * params from the end of the collection working backwards (`after` becomes
     * `before`, `offset` skips the nodes at the end instead of the start,
     * `limit` is applied such that it limits from the end rather than from the
     * start). One way to implement this is to reverse the ordering, apply the
     * params as normal, and then reverse the ordering back again. Do **NOT**
     * return the list in reverse order.
     *
     * It is recommended that if you do not support `reverse` that you do not
     * expose the reverse pagination arguments (`before` and `last`) as part of a
     * GraphQL schema, since `connection()` will need to fetch your entire
     * collection to implement pagination.
     */
    reverse?: boolean;
    /**
     * If you support cursor pagination you must provide a
     * `.cursorForItem($item)` method that returns the cursor to use for
     * the given item, and we will assume yout support (and you must support) the
     * `after` parameter in `PaginationParams`.
     *
     * If this is true but `reverse` is not, we will reject all attempts to do
     * reverse pagination (because a cursor cannot be applied by us, even if we
     * were to fetch the full collection). You should therefore strongly consider
     * implementing `reverse` support, or simply do not allow reverse pagination
     * through your schema.
     */
    cursor?: boolean;
    /**
     * If you support offset pagination, set this to true. If you also set
     * `cursor` to true, then the offset must apply _from_ the cursor; if have
     * `cursor` enabled and do not support combining `offset` and `cursor`
     * together then you should set `offset` to `false` (and raise an issue so we
     * can add new options for that).
     */
    offset?: boolean;
    /**
     * Set this if your collection supports every feature of GraphQL pagination,
     * and implements the `ConnectionHandlingStep` interface. Instead of caling
     * `applyPagination` as we do for `ConnectionOptimizedStep`, we will pass
     * each individual parameter through to your collection step. Your collection
     * step is also responsible for implementing the
     * `hasNextPage`/`hasPreviousPage` logic, and must yield from execution an
     * object conforming to `ConnectionHandlingResult`.
     */
    full?: boolean;
}
export interface PaginationParams<TCursorValue = string> {
    /**
     * Fetch only this many rows. If null, apply no limit.
     */
    limit: number | null;
    /**
     * Skip this many rows. Always null unless `paginationSupport.offset` is `true`.
     *
     * If `after` is set, the offset applies after the `after`.
     */
    offset: number | null;
    /**
     * A cursor representing the "exclusive lower bound" for the results -
     * skip over anything up to and including this cursor. Always `null` unless
     * `paginationSupport.cursor` is `true`.
     *
     * Note: if `reverse` is `true`, this applies in the reverse direction (i.e.
     * if `after` becomes equivalent to the `before` argument exposed through
     * GraphQL).
     */
    after: TCursorValue | null;
    /**
     * If we're paginating backwards then the collection should apply the other
     * parameters (`limit`, `offset`, `after`) in reverse (from the end of the
     * collection). Always `false` unless `paginationSupport.cursor` is `true`.
     */
    reverse: boolean;
    /**
     * This will be non-null if it's desirable for the underlying step to stream.
     * Underlying step should detect whether to stream or not from
     * `(executionDetails.stream ?? params?.stream)` - works exactly as
     * `executionDetails.stream`.
     */
    stream: ExecutionDetailsStream | null;
}
/**
 * Describes what a plan may implement for ConnectionStep to be able to
 * utilise it in the most optimal way.
 *
 * Implementing this is optional, but:
 *
 * - `paginationSupport` should be set (even an empty object) if your data
 *   source supports setting a limit
 * - `paginationSupport.reverse` should be implemented if you plan to support
 *   reverse pagination (`before`, `last` arguments in GraphQL pagination)
 * - either `paginationSupport.offset` or `paginationSupport.cursor` are
 *   highly recommended for efficiency
 *
 * Be sure to read the documentation of the features you indicate support for!
 *
 * @param TItem - The data represented by an individual list item
 * @param TNodeStep - A derivative of TEdgeStep that represents the _node_ itself. Defaults to TEdgeStep.
 * @param TEdgeStep - Represents an item in the collection, typically equivalent to an _edge_.
 * @param TCursorValue - If your step wants us to parse the cursor for you, the result of the parse. Useful for throwing an error before the fetch if the cursor is invalid.
 */
export interface ConnectionOptimizedStep<TItem, TNodeStep extends Step = Step<TItem>, TEdgeStep extends EdgeCapableStep<TItem, TNodeStep> = EdgeStep<TItem, TNodeStep>, TCursorValue = string> extends Step {
    /**
     * If set, we assume that you support at least `limit` pagination, even on an
     * empty object.
     *
     * Must not be set without also implementing `applyPagination` and `connectionClone`.
     */
    paginationSupport: PaginationFeatures;
    /**
     * Receives the pagination parameters that were declared to be supported by
     * `paginationSupport`.
     *
     * Must not be implemented without also adding `paginationSupport` and `connectionClone`.
     */
    applyPagination($params: Step<PaginationParams<TCursorValue>>): void;
    /**
     * Clone the plan, ignoring the pagination parameters.
     *
     * Useful for implementing things like `totalCount` or aggregates.
     */
    connectionClone?(...args: any[]): ConnectionOptimizedStep<TItem, TNodeStep, TEdgeStep, TCursorValue>;
    /**
     * Optionally implement this and we will parse the cursor for you before
     * handing it to `applyPagination`.
     *
     * Must not be added unless `paginationSupport.cursor` is `true`.
     */
    parseCursor?($cursor: Step<Maybe<string>>): Step<Maybe<TCursorValue>>;
    /**
     * If the `$item` represents an edge rather than the node, return the node to
     * use instead.
     */
    nodeForItem?($item: Step<TItem>): TNodeStep;
    /**
     * Turn a value from the list into an item step
     */
    edgeForItem?($item: Step<MaybeIndexed<TItem>>): TEdgeStep;
    /**
     * Used as a fallback if nodeForItem isn't implemented.
     */
    listItem?($item: Step<TItem>): TNodeStep;
    /**
     * Given the $item, return a step representing the cursor.
     *
     * Must be implemented if and only if `paginationSupport.cursor` is `true`.
     */
    cursorForItem?($item: Step<TItem>): Step<string>;
}
/** The result of a "ConnectionHandlingStep". */
export interface ConnectionHandlingResult<TItem> {
    hasNextPage: PromiseOrDirect<boolean>;
    hasPreviousPage: PromiseOrDirect<boolean>;
    items: ReadonlyArray<TItem> | AsyncIterable<TItem>;
}
export interface ConnectionHandlingStep<TItem, TNodeStep extends Step = Step<TItem>, TEdgeStep extends EdgeCapableStep<TItem, TNodeStep> = EdgeStep<TItem, TNodeStep>, TCursorValue = string> extends Step<Maybe<ConnectionHandlingResult<TItem>>>, Pick<ConnectionOptimizedStep<TItem, TNodeStep, TEdgeStep, TCursorValue>, "parseCursor" | "nodeForItem" | "edgeForItem" | "listItem">, Required<Pick<ConnectionOptimizedStep<TItem, TNodeStep, TEdgeStep, TCursorValue>, "cursorForItem">> {
    /**
     * Clone the plan, ignoring the pagination parameters.
     *
     * Useful for implementing things like `totalCount` or aggregates.
     */
    connectionClone?(...args: any[]): ConnectionHandlingStep<TItem, TNodeStep, TEdgeStep, TCursorValue>;
    paginationSupport: {
        full: true;
    };
    setFirst($first: Step<Maybe<number>>): void;
    setLast($last: Step<Maybe<number>>): void;
    setOffset($offset: Step<Maybe<number>>): void;
    setBefore($before: Step<Maybe<TCursorValue>>): void;
    setAfter($after: Step<Maybe<TCursorValue>>): void;
    /**
     * Called when `hasNextPage`/`hasPreviousPage` are requested.
     */
    setNeedsHasMore(): void;
    /**
     * Passes the stream options that this collection is accessed with. This may
     * happen more than once (e.g. if you have
     * `nodes @stream(...) {...}, edges @stream(...) {...}`). If so, `null` wins,
     * and failing that, the largest `initialCount` wins.
     *
     * WARNING: This may be called more than once!
     */
    addStreamDetails?($streamDetails: Step<ExecutionDetailsStream | null> | null): void;
}
interface Indexed<TItem> {
    index: number;
    item: TItem;
}
type MaybeIndexed<TItem> = TItem | Indexed<TItem>;
interface ConnectionResult<TItem> {
    hasNextPage: PromiseOrDirect<boolean>;
    hasPreviousPage: PromiseOrDirect<boolean>;
    items: ReadonlyArray<MaybeIndexed<TItem>> | Iterable<MaybeIndexed<TItem>> | AsyncIterable<MaybeIndexed<TItem>>;
}
interface EdgeCapableStep<TItem, TNodeStep extends Step, TEdgeDataStep extends Step = Step<TItem>> extends Step {
    node(): TNodeStep;
    cursor(): Step<string>;
    data(): TEdgeDataStep;
}
export type StepRepresentingList<TItem, TNodeStep extends Step = Step<TItem>, TEdgeStep extends EdgeCapableStep<TItem, TNodeStep> = EdgeStep<TItem, TNodeStep>, TCursorValue = string> = ConnectionOptimizedStep<TItem, TNodeStep, TEdgeStep, TCursorValue> | StepWithItems<TItem> | Step<Maybe<readonly TItem[]>>;
/**
 * Handles GraphQL cursor pagination in a standard and consistent way
 * indepdenent of data source.
 */
export declare class ConnectionStep<TItem, TNodeStep extends Step = Step<TItem>, TEdgeDataStep extends Step = Step<TItem>, TEdgeStep extends EdgeCapableStep<TItem, TNodeStep, TEdgeDataStep> = EdgeStep<TItem, TNodeStep, TEdgeDataStep>, TCursorValue = string, TCollectionStep extends StepRepresentingList<TItem, TNodeStep, TEdgeStep, TCursorValue> = StepRepresentingList<TItem, TNodeStep, TEdgeStep, TCursorValue>> extends Step<ConnectionResult<TItem> | null> {
    static $$export: {
        moduleName: string;
        exportName: string;
    };
    isSyncAndSafe: boolean;
    private neededCollection;
    private collectionDepId;
    /**
     * null = unknown
     */
    private _mightStream;
    private _firstDepId;
    private _lastDepId;
    private _offsetDepId;
    private _beforeDepId;
    private _afterDepId;
    /** If `null` we **must not** mess with this.getSubplan() */
    private collectionPaginationSupport;
    /** If the user asks for details of `hasNextPage`/`hasPreviousPage`, then fetch one extra */
    private needsHasMore;
    private needsCursor;
    private paramsDepId;
    edgeDataPlan: ($rawItem: Step<TItem>) => TEdgeDataStep;
    constructor(subplan: TCollectionStep, params?: Omit<ConnectionParams<any, TItem, TEdgeDataStep>, "fieldArgs">);
    mightStream(): boolean;
    getSubplan(): TCollectionStep;
    private _getSubplan;
    /**
     * This represents a single page from the collection - not only have
     * conditions and ordering been applied but we've also applied the pagination
     * constraints (before, after, first, last, offset). It's useful for
     * returning the actual edges and nodes of the connection.
     *
     * This cannot be called before the arguments have been finalized.
     */
    private setupSubplanWithPagination;
    private getHandler;
    toStringMeta(): string;
    setNeedsHasMore(): void;
    getFirst(): Step<number | null | undefined> | null;
    setFirst(first: Step<number | null | undefined> | number): void;
    getLast(): Step<number | null | undefined> | null;
    setLast(last: Step<number | null | undefined> | number): void;
    getOffset(): Step<number | null | undefined> | null;
    setOffset(offset: Step<number | null | undefined> | number): void;
    getBefore(): Step<Maybe<TCursorValue>> | null;
    setBefore($beforePlan: Step<Maybe<string>>): void;
    getAfter(): Step<Maybe<TCursorValue>> | null;
    setAfter($afterPlan: Step<Maybe<string>>): void;
    /**
     * This represents the entire collection with conditions and ordering
     * applied, but without any pagination constraints (before, after, first,
     * last, offset) applied. It's useful for the following:
     *
     * - performing aggregates e.g. totalCount across the entire collection
     * - determining fields for pageInfo, e.g. is there a next/previous page
     *
     * This cannot be called before the arguments have been finalized.
     */
    cloneSubplanWithoutPagination(...args: Parameters<TCollectionStep extends ConnectionOptimizedStep<TItem, TNodeStep, TEdgeStep, TCursorValue> | ConnectionHandlingStep<TItem, TNodeStep, TEdgeStep, TCursorValue> ? Exclude<TCollectionStep["connectionClone"], undefined> : never>): TCollectionStep;
    private paginationParams;
    /**
     * Subplans may call this from their `setBefore`/`setAfter`/etc plans in order
     * to add a dependency to us, which is typically useful for adding validation
     * functions so that they are thrown "earlier", avoiding error bubbling.
     */
    addValidation(callback: () => Step): void;
    get(fieldName: string): Step<any>;
    listItem($rawItem: Step<TItem>): TNodeStep;
    getUnindexedItem($rawItem: Step<MaybeIndexed<TItem>>): Step<TItem>;
    nodePlan: ($rawItem: Step<MaybeIndexed<TItem>>) => TNodeStep;
    edgePlan: ($rawItem: Step<MaybeIndexed<TItem>>) => TEdgeStep | EdgeStep<TItem, TNodeStep, TEdgeStep>;
    private captureStream;
    edges(): Step;
    nodes(): import("./listTransform.ts").__ListTransformStep<any, any, any, any>;
    items(): import("./listTransform.ts").__ListTransformStep<any, any, any, any>;
    cursorPlan($rawItem: Step<MaybeIndexed<TItem>>): Step<string> | import("./lambda.ts").LambdaStep<readonly [number, number, number], string>;
    pageInfo(): PageInfoStep;
    optimize(): this | ConstantStep<ConnectionResult<never>>;
    deduplicatedWith(replacement: ConnectionStep<any, any, any, any, any>): void;
    execute({ values, indexMap, }: ExecutionDetails): GrafastResultsList<ConnectionResult<TItem> | null>;
}
export declare class EdgeStep<TItem, TNodeStep extends Step = Step, TEdgeDataStep extends Step = Step<TItem>> extends UnbatchedStep {
    static $$export: {
        moduleName: string;
        exportName: string;
    };
    isSyncAndSafe: boolean;
    private connectionRefId;
    constructor($connection: ConnectionStep<TItem, TNodeStep, any, any, any>, $rawItem: Step<MaybeIndexed<TItem>>);
    get(fieldName: string): Step<string> | ConstantStep<undefined> | TNodeStep | TEdgeDataStep;
    private getConnectionStep;
    private getRawItemStep;
    private getItemStep;
    data(): TEdgeDataStep;
    node(): TNodeStep;
    private cursorDepId;
    cursor(): Step<string>;
    deduplicate(_peers: EdgeStep<any, any>[]): EdgeStep<TItem, TNodeStep>[];
    unbatchedExecute(_extra: UnbatchedExecutionExtra, record: any, cursor: any): any;
}
interface ConnectionParams<TObj extends BaseGraphQLArguments = any, TItem = any, TEdgeDataStep extends Step = Step<TItem>> {
    fieldArgs?: FieldArgs<TObj>;
    edgeDataPlan?: ($item: Step<TItem>) => TEdgeDataStep;
}
/**
 * Wraps a collection fetch to provide the utilities for working with GraphQL
 * cursor connections.
 */
export declare function connection<TItem, TNodeStep extends Step = Step<TItem>, TEdgeDataStep extends Step = Step<TItem>, TEdgeStep extends EdgeCapableStep<TItem, TNodeStep, TEdgeDataStep> = EdgeStep<TItem, TNodeStep, TEdgeDataStep>, TCursorValue = any, TCollectionStep extends StepRepresentingList<TItem, TNodeStep, TEdgeStep, TCursorValue> = StepRepresentingList<TItem, TNodeStep, TEdgeStep, TCursorValue>, TFieldArgs extends BaseGraphQLArguments = any>(step: TCollectionStep, params?: ConnectionParams<TFieldArgs, TItem, TEdgeDataStep>): ConnectionStep<TItem, TNodeStep, TEdgeDataStep, TEdgeStep, TCursorValue, TCollectionStep>;
interface StepWithItems<TItem = any> extends Step {
    items(): Step<Maybe<ReadonlyArray<TItem>>>;
}
export type ItemsStep<TStep extends StepRepresentingList<any>> = TStep extends StepWithItems ? ReturnType<TStep["items"]> : TStep;
export declare function itemsOrStep<T extends Step<Maybe<readonly any[]>> | StepWithItems>($step: T): Step<Maybe<readonly any[]>>;
export declare class ConnectionParamsStep<TCursorValue> extends UnbatchedStep<PaginationParams<TCursorValue>> {
    static $$export: {
        moduleName: string;
        exportName: string;
    };
    /** sync and safe because it's unary; an error thrown for one is thrown for all */
    isSyncAndSafe: boolean;
    private needsHasMore;
    private firstDepId;
    private lastDepId;
    private offsetDepId;
    private beforeDepId;
    private afterDepId;
    private streamDetailsDepIds;
    private paginationSupport;
    constructor(paginationSupport: PaginationFeatures | null);
    setFirst($first: Step<Maybe<number>>): void;
    setLast($last: Step<Maybe<number>>): void;
    setOffset($offset: Step<Maybe<number>>): void;
    setBefore($before: Step<Maybe<TCursorValue>>): void;
    setAfter($after: Step<Maybe<TCursorValue>>): void;
    setNeedsHasMore(): void;
    addStreamDetails($details: Step<ExecutionDetailsStream | null> | null): void;
    /**
     * True if it's possible this'll stream, false if we've not
     * been told anything about streaming.
     */
    mightStream(): boolean;
    deduplicate(peers: ConnectionParamsStep<any>[]): ConnectionParamsStep<TCursorValue>[];
    deduplicatedWith(replacement: ConnectionParamsStep<any>): void;
    unbatchedExecute(extra: UnbatchedExecutionExtra, ...values: any[]): PaginationParams<TCursorValue>;
}
declare class PageInfoStep extends UnbatchedStep<ConnectionResult<any>> {
    static $$export: {
        moduleName: string;
        exportName: string;
    };
    isSyncAndSafe: boolean;
    constructor($connection: ConnectionStep<any, any, any, any, any, any>);
    get(key: string): Step<string> | ConstantStep<undefined> | import("./lambda.ts").LambdaStep<readonly [number, number, number], string> | import("./access.ts").AccessStep<PromiseOrDirect<boolean> | undefined>;
    unbatchedExecute(_extra: UnbatchedExecutionExtra, _connection: ConnectionResult<any>): ConnectionResult<any>;
}
export declare class ConnectionItemsStep extends Step {
    static $$export: {
        moduleName: string;
        exportName: string;
    };
    isSyncAndSafe: boolean;
    cloneStreams: boolean;
    constructor($connection: ConnectionStep<any, any, any, any, any, any>);
    getConnection(): ConnectionStep<any, any, any, any, any, any>;
    optimize(_options: StepOptimizeOptions): Step;
    deduplicate(_peers: readonly ConnectionItemsStep[]): readonly ConnectionItemsStep[];
    execute(executionDetails: ExecutionDetails): readonly any[];
}
export {};
//# sourceMappingURL=connection.d.ts.map