import type { LogContext } from '@rocicorp/logger';
import type { Downstream } from '../../../../zero-protocol/src/down.ts';
import { type MutationID, type PushBody } from '../../../../zero-protocol/src/push.ts';
import { type ZeroConfig } from '../../config/zero-config.ts';
import type { Source } from '../../types/streams.ts';
import { Subscription } from '../../types/subscription.ts';
import type { HandlerResult, StreamResult } from '../../workers/connection.ts';
import type { RefCountedService, Service } from '../service.ts';
export interface Pusher extends RefCountedService {
    readonly pushURL: string | undefined;
    initConnection(clientID: string, wsID: string, userPushURL: string | undefined, userPushHeaders: Record<string, string> | undefined, onAuthFailure?: (() => void) | undefined): Source<Downstream>;
    enqueuePush(clientID: string, push: PushBody, auth: string | undefined, httpCookie: string | undefined, origin: string | undefined): HandlerResult;
    ackMutationResponses(upToID: MutationID): Promise<void>;
    deleteClientMutations(clientIDs: string[]): Promise<void>;
}
type Config = Pick<ZeroConfig, 'app' | 'shard'>;
/**
 * Receives push messages from zero-client and forwards
 * them the the user's API server.
 *
 * If the user's API server is taking too long to process
 * the push, the PusherService will add the push to a queue
 * and send pushes in bulk the next time the user's API server
 * is available.
 *
 * - One PusherService exists per client group.
 * - Mutations for a given client are always sent in-order
 * - Mutations for different clients in the same group may be interleaved
 */
export declare class PusherService implements Service, Pusher {
    #private;
    readonly id: string;
    constructor(appConfig: Config, pushConfig: ZeroConfig['push'] & {
        url: string[];
    }, lc: LogContext, clientGroupID: string);
    get pushURL(): string | undefined;
    initConnection(clientID: string, wsID: string, userPushURL: string | undefined, userPushHeaders: Record<string, string> | undefined, onAuthFailure?: (() => void) | undefined): Subscription<["deleteClients", {
        readonly clientIDs?: readonly string[] | undefined;
        readonly clientGroupIDs?: readonly string[] | undefined;
    }] | ["connected", {
        timestamp?: number | undefined;
        wsid: string;
    }] | import("../../../../zero-protocol/src/error.ts").ErrorMessage | ["transformError", ({
        message?: string | undefined;
        details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
        error: "app";
        id: string;
        name: string;
    } | {
        details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
        error: "parse";
        id: string;
        name: string;
        message: string;
    })[]] | ["pushResponse", {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "unsupportedPushVersion";
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "unsupportedSchemaVersion";
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "http";
        status: number;
        details: string;
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "zeroPusher";
        details: string;
    } | {
        mutations: {
            id: {
                id: number;
                clientID: string;
            };
            result: {
                message?: string | undefined;
                details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                error: "app";
            } | {
                details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                error: "oooMutation" | "alreadyProcessed";
            } | {
                data?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
            };
        }[];
    }] | ["inspect", {
        id: string;
        op: "queries";
        value: {
            metrics?: {
                'query-materialization-server': [number, ...number[]];
                'query-update-server': [number, ...number[]];
            } | null | undefined;
            clientID: string;
            queryID: string;
            ast: import("../../../../zero-protocol/src/ast.ts").AST | null;
            name: string | null;
            args: readonly import("../../../../shared/src/json.ts").ReadonlyJSONValue[] | null;
            got: boolean;
            deleted: boolean;
            ttl: number;
            inactivatedAt: number | null;
            rowCount: number;
        }[];
    } | {
        id: string;
        op: "metrics";
        value: {
            'query-materialization-server': [number, ...number[]];
            'query-update-server': [number, ...number[]];
        };
    } | {
        id: string;
        op: "authenticated";
        value: boolean;
    } | {
        id: string;
        op: "analyze-query";
        value: {
            syncedRows?: Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]> | undefined;
            elapsed?: number | undefined;
            afterPermissions?: string | undefined;
            vendedRowCounts?: Record<string, Record<string, number>> | undefined;
            vendedRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
            sqlitePlans?: Record<string, string[]> | undefined;
            readRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
            readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
            readRowCount?: number | undefined;
            dbScansByQuery?: Record<string, Record<string, number>> | undefined;
            joinPlans?: ({
                type: "attempt-start";
                attemptNumber: number;
                totalAttempts: number;
            } | {
                type: "connection-costs";
                attemptNumber: number;
                costs: {
                    connection: string;
                    cost: number;
                    costEstimate: {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    };
                    pinned: boolean;
                    constraints: Record<string, Record<string, unknown> | null>;
                    constraintCosts: Record<string, {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    }>;
                }[];
            } | {
                type: "connection-selected";
                attemptNumber: number;
                connection: string;
                cost: number;
                isRoot: boolean;
            } | {
                type: "constraints-propagated";
                attemptNumber: number;
                connectionConstraints: {
                    connection: string;
                    constraints: Record<string, Record<string, unknown> | null>;
                    constraintCosts: Record<string, {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    }>;
                }[];
            } | {
                type: "plan-complete";
                attemptNumber: number;
                totalCost: number;
                flipPattern: number;
                joinStates: {
                    join: string;
                    type: "semi" | "flipped" | "unflippable";
                }[];
            } | {
                type: "plan-failed";
                attemptNumber: number;
                reason: string;
            } | {
                type: "best-plan-selected";
                bestAttemptNumber: number;
                totalCost: number;
                flipPattern: number;
                joinStates: {
                    join: string;
                    type: "semi" | "flipped" | "unflippable";
                }[];
            } | {
                attemptNumber?: number | undefined;
                filters?: import("../../../../zero-protocol/src/ast.ts").Condition | undefined;
                ordering?: readonly (readonly [string, "desc" | "asc"])[] | undefined;
                joinType?: "semi" | "flipped" | "unflippable" | undefined;
                type: "node-cost";
                nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
                node: string;
                branchPattern: number[];
                downstreamChildSelectivity: number;
                costEstimate: {
                    limit?: number | undefined;
                    startupCost: number;
                    scanEst: number;
                    cost: number;
                    returnedRows: number;
                    selectivity: number;
                };
            } | {
                attemptNumber?: number | undefined;
                constraint?: Record<string, unknown> | null | undefined;
                type: "node-constraint";
                nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
                node: string;
                branchPattern: number[];
                from: string;
            })[] | undefined;
            warnings: string[];
            syncedRowCount: number;
            start: number;
            end: number;
        };
    } | {
        id: string;
        op: "error";
        value: string;
    } | {
        id: string;
        op: "version";
        value: string;
    }] | ["pokeStart", {
        schemaVersions?: {
            minSupportedVersion: number;
            maxSupportedVersion: number;
        } | undefined;
        timestamp?: number | undefined;
        pokeID: string;
        baseCookie: string | null;
    }] | ["pokePart", {
        lastMutationIDChanges?: Record<string, number> | undefined;
        desiredQueriesPatches?: Record<string, ({
            ttl?: number | undefined;
            op: "put";
            hash: string;
        } | {
            op: "del";
            hash: string;
        } | {
            op: "clear";
        })[]> | undefined;
        gotQueriesPatch?: ({
            ttl?: number | undefined;
            op: "put";
            hash: string;
        } | {
            op: "del";
            hash: string;
        } | {
            op: "clear";
        })[] | undefined;
        rowsPatch?: ({
            op: "put";
            tableName: string;
            value: Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
        } | {
            merge?: import("../../../../shared/src/json.ts").ReadonlyJSONObject | undefined;
            constrain?: string[] | undefined;
            op: "update";
            tableName: string;
            id: Readonly<Record<string, string | number | boolean>>;
        } | {
            op: "del";
            tableName: string;
            id: Readonly<Record<string, string | number | boolean>>;
        } | {
            op: "clear";
        })[] | undefined;
        mutationsPatch?: ({
            op: "put";
            mutation: {
                id: {
                    id: number;
                    clientID: string;
                };
                result: {
                    message?: string | undefined;
                    details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                    error: "app";
                } | {
                    details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                    error: "oooMutation" | "alreadyProcessed";
                } | {
                    data?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                };
            };
        } | {
            op: "del";
            id: {
                id: number;
                clientID: string;
            };
        })[] | undefined;
        pokeID: string;
    }] | ["pokeEnd", {
        cancel?: boolean | undefined;
        pokeID: string;
        cookie: string;
    }] | ["pong", {}] | ["pull", {
        cookie: string;
        requestID: string;
        lastMutationIDChanges: Record<string, number>;
    }], ["deleteClients", {
        readonly clientIDs?: readonly string[] | undefined;
        readonly clientGroupIDs?: readonly string[] | undefined;
    }] | ["connected", {
        timestamp?: number | undefined;
        wsid: string;
    }] | import("../../../../zero-protocol/src/error.ts").ErrorMessage | ["transformError", ({
        message?: string | undefined;
        details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
        error: "app";
        id: string;
        name: string;
    } | {
        details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
        error: "parse";
        id: string;
        name: string;
        message: string;
    })[]] | ["pushResponse", {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "unsupportedPushVersion";
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "unsupportedSchemaVersion";
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "http";
        status: number;
        details: string;
    } | {
        mutationIDs?: {
            id: number;
            clientID: string;
        }[] | undefined;
        error: "zeroPusher";
        details: string;
    } | {
        mutations: {
            id: {
                id: number;
                clientID: string;
            };
            result: {
                message?: string | undefined;
                details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                error: "app";
            } | {
                details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                error: "oooMutation" | "alreadyProcessed";
            } | {
                data?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
            };
        }[];
    }] | ["inspect", {
        id: string;
        op: "queries";
        value: {
            metrics?: {
                'query-materialization-server': [number, ...number[]];
                'query-update-server': [number, ...number[]];
            } | null | undefined;
            clientID: string;
            queryID: string;
            ast: import("../../../../zero-protocol/src/ast.ts").AST | null;
            name: string | null;
            args: readonly import("../../../../shared/src/json.ts").ReadonlyJSONValue[] | null;
            got: boolean;
            deleted: boolean;
            ttl: number;
            inactivatedAt: number | null;
            rowCount: number;
        }[];
    } | {
        id: string;
        op: "metrics";
        value: {
            'query-materialization-server': [number, ...number[]];
            'query-update-server': [number, ...number[]];
        };
    } | {
        id: string;
        op: "authenticated";
        value: boolean;
    } | {
        id: string;
        op: "analyze-query";
        value: {
            syncedRows?: Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]> | undefined;
            elapsed?: number | undefined;
            afterPermissions?: string | undefined;
            vendedRowCounts?: Record<string, Record<string, number>> | undefined;
            vendedRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
            sqlitePlans?: Record<string, string[]> | undefined;
            readRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
            readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
            readRowCount?: number | undefined;
            dbScansByQuery?: Record<string, Record<string, number>> | undefined;
            joinPlans?: ({
                type: "attempt-start";
                attemptNumber: number;
                totalAttempts: number;
            } | {
                type: "connection-costs";
                attemptNumber: number;
                costs: {
                    connection: string;
                    cost: number;
                    costEstimate: {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    };
                    pinned: boolean;
                    constraints: Record<string, Record<string, unknown> | null>;
                    constraintCosts: Record<string, {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    }>;
                }[];
            } | {
                type: "connection-selected";
                attemptNumber: number;
                connection: string;
                cost: number;
                isRoot: boolean;
            } | {
                type: "constraints-propagated";
                attemptNumber: number;
                connectionConstraints: {
                    connection: string;
                    constraints: Record<string, Record<string, unknown> | null>;
                    constraintCosts: Record<string, {
                        limit?: number | undefined;
                        startupCost: number;
                        scanEst: number;
                        cost: number;
                        returnedRows: number;
                        selectivity: number;
                    }>;
                }[];
            } | {
                type: "plan-complete";
                attemptNumber: number;
                totalCost: number;
                flipPattern: number;
                joinStates: {
                    join: string;
                    type: "semi" | "flipped" | "unflippable";
                }[];
            } | {
                type: "plan-failed";
                attemptNumber: number;
                reason: string;
            } | {
                type: "best-plan-selected";
                bestAttemptNumber: number;
                totalCost: number;
                flipPattern: number;
                joinStates: {
                    join: string;
                    type: "semi" | "flipped" | "unflippable";
                }[];
            } | {
                attemptNumber?: number | undefined;
                filters?: import("../../../../zero-protocol/src/ast.ts").Condition | undefined;
                ordering?: readonly (readonly [string, "desc" | "asc"])[] | undefined;
                joinType?: "semi" | "flipped" | "unflippable" | undefined;
                type: "node-cost";
                nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
                node: string;
                branchPattern: number[];
                downstreamChildSelectivity: number;
                costEstimate: {
                    limit?: number | undefined;
                    startupCost: number;
                    scanEst: number;
                    cost: number;
                    returnedRows: number;
                    selectivity: number;
                };
            } | {
                attemptNumber?: number | undefined;
                constraint?: Record<string, unknown> | null | undefined;
                type: "node-constraint";
                nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
                node: string;
                branchPattern: number[];
                from: string;
            })[] | undefined;
            warnings: string[];
            syncedRowCount: number;
            start: number;
            end: number;
        };
    } | {
        id: string;
        op: "error";
        value: string;
    } | {
        id: string;
        op: "version";
        value: string;
    }] | ["pokeStart", {
        schemaVersions?: {
            minSupportedVersion: number;
            maxSupportedVersion: number;
        } | undefined;
        timestamp?: number | undefined;
        pokeID: string;
        baseCookie: string | null;
    }] | ["pokePart", {
        lastMutationIDChanges?: Record<string, number> | undefined;
        desiredQueriesPatches?: Record<string, ({
            ttl?: number | undefined;
            op: "put";
            hash: string;
        } | {
            op: "del";
            hash: string;
        } | {
            op: "clear";
        })[]> | undefined;
        gotQueriesPatch?: ({
            ttl?: number | undefined;
            op: "put";
            hash: string;
        } | {
            op: "del";
            hash: string;
        } | {
            op: "clear";
        })[] | undefined;
        rowsPatch?: ({
            op: "put";
            tableName: string;
            value: Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
        } | {
            merge?: import("../../../../shared/src/json.ts").ReadonlyJSONObject | undefined;
            constrain?: string[] | undefined;
            op: "update";
            tableName: string;
            id: Readonly<Record<string, string | number | boolean>>;
        } | {
            op: "del";
            tableName: string;
            id: Readonly<Record<string, string | number | boolean>>;
        } | {
            op: "clear";
        })[] | undefined;
        mutationsPatch?: ({
            op: "put";
            mutation: {
                id: {
                    id: number;
                    clientID: string;
                };
                result: {
                    message?: string | undefined;
                    details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                    error: "app";
                } | {
                    details?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                    error: "oooMutation" | "alreadyProcessed";
                } | {
                    data?: import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined;
                };
            };
        } | {
            op: "del";
            id: {
                id: number;
                clientID: string;
            };
        })[] | undefined;
        pokeID: string;
    }] | ["pokeEnd", {
        cancel?: boolean | undefined;
        pokeID: string;
        cookie: string;
    }] | ["pong", {}] | ["pull", {
        cookie: string;
        requestID: string;
        lastMutationIDChanges: Record<string, number>;
    }]>;
    enqueuePush(clientID: string, push: PushBody, auth: string | undefined, httpCookie: string | undefined, origin: string | undefined): Exclude<HandlerResult, StreamResult>;
    ackMutationResponses(upToID: MutationID): Promise<void>;
    deleteClientMutations(clientIDs: string[]): Promise<void>;
    ref(): void;
    unref(): void;
    hasRefs(): boolean;
    run(): Promise<void>;
    stop(): Promise<void>;
}
type PusherEntry = {
    push: PushBody;
    auth: string | undefined;
    httpCookie: string | undefined;
    origin: string | undefined;
    clientID: string;
};
type PusherEntryOrStop = PusherEntry | 'stop';
/**
 * Pushes for different clientIDs could theoretically be interleaved.
 *
 * In order to do efficient batching to the user's API server,
 * we collect all pushes for the same clientID into a single push.
 */
export declare function combinePushes(entries: readonly (PusherEntryOrStop | undefined)[]): [PusherEntry[], boolean];
export {};
//# sourceMappingURL=pusher.d.ts.map