import type { LogContext } from '@rocicorp/logger';
import type { InitConnectionBody } from '../../../../zero-protocol/src/connect.ts';
import { type Auth, type ValidateLegacyJWT } from '../../auth/auth.ts';
import type { ConnectParams } from '../../workers/connect-params.ts';
import type { UpdateAuthBody } from '../../../../zero-protocol/src/update-auth.ts';
import type { ZeroConfig } from '../../config/zero-config.ts';
export type ConnectionState = 'provisional' | 'validated';
/**
 * Identifies one live websocket for a client slot.
 */
export type ConnectionSelector = {
    readonly clientID: string;
    readonly wsID: string;
};
type FetchConfig = ZeroConfig['query'];
export type HeaderOptions = {
    apiKey?: string | undefined;
    customHeaders?: Record<string, string> | undefined;
    allowedClientHeaders?: readonly string[] | undefined;
    cookie?: string | undefined;
    origin?: string | undefined;
};
export type ConnectionFetchContext = {
    url: string | undefined;
    allowedUrlPatterns: URLPattern[] | undefined;
    headerOptions: HeaderOptions;
};
/**
 * A snapshot of one live connection tracked by the manager.
 *
 * `revalidateAt` is only populated while the connection is `validated`.
 */
export type ConnectionContext = {
    state: ConnectionState;
    readonly clientID: string;
    readonly wsID: string;
    readonly userID: string | undefined;
    auth: Auth | undefined;
    readonly profileID: string | null;
    readonly baseCookie: string | null;
    readonly protocolVersion: number;
    revision: number;
    revalidateAt: number | undefined;
    readonly insertionOrder: number;
    readonly queryContext: ConnectionFetchContext;
    readonly pushContext: ConnectionFetchContext;
};
/**
 * Group-scoped auth state shared across the live connections.
 *
 * The background connection is the validated connection currently used for
 * shared background work. Retransform happens on a group level, and uses
 * the background connection's credential to refetch the latest queries.
 *
 * Since auth can be pinned to logged-out users, `userID` can be undefined
 * even after validation.
 */
export type GroupAuthState = {
    userID: string | undefined;
    validated: boolean;
    backgroundConnection: ConnectionSelector | undefined;
    retransformAt: number | undefined;
    maintenanceNotBeforeAt: number | undefined;
};
export type ConnectionContextManager = {
    registerConnection(selector: ConnectionSelector, connectParams: ConnectParams, auth?: Auth): Readonly<ConnectionContext>;
    initConnection(selector: ConnectionSelector, body: InitConnectionBody): Readonly<ConnectionContext>;
    updateAuth(selector: ConnectionSelector, body: UpdateAuthBody): Promise<Readonly<ConnectionContext>>;
    validateConnection(selector: ConnectionSelector, revision: number): Readonly<{
        connection: ConnectionContext;
        group: GroupAuthState;
    }> | undefined;
    failConnection(selector: ConnectionSelector, revision: number): Readonly<ConnectionContext> | undefined;
    closeConnection(selector: ConnectionSelector): Readonly<ConnectionContext> | undefined;
    markBackgroundRetransformSuccess(selector: ConnectionSelector, revision: number): void;
    deferMaintenance(kind: 'revalidate' | 'retransform'): void;
    getConnectionContext(selector: ConnectionSelector): Readonly<ConnectionContext> | undefined;
    mustGetConnectionContext(selector: ConnectionSelector): Readonly<ConnectionContext>;
    getBackgroundConnectionContext(): Readonly<ConnectionContext> | undefined;
    mustGetBackgroundConnectionContext(): Readonly<ConnectionContext>;
    getGroupState(): Readonly<GroupAuthState>;
    planMaintenance(): {
        dueRevalidations: Readonly<ConnectionContext>[];
        dueRetransform: boolean;
        earliestDeadlineAt: number | undefined;
    };
};
/**
 * State machine for the auth state of a single `ViewSyncerService`.
 *
 * Connections are registered as `provisional`, optionally backfilled with
 * `initConnection` metadata, and then promoted to `validated` once their
 * stored `userID` is confirmed as valid. The manager also tracks which
 * validated connection currently serves as the group's background connection.
 *
 * This is intentionally side-effect free.
 */
export declare class ConnectionContextManagerImpl implements ConnectionContextManager {
    #private;
    constructor(lc: LogContext, revalidateIntervalSeconds?: number, retransformIntervalSeconds?: number, queryConfig?: FetchConfig, pushConfig?: FetchConfig, validateLegacyJWT?: ValidateLegacyJWT, now?: () => number);
    /**
     * Creates or replaces the live record for a websocket connection.
     *
     * Re-registering the same `clientID` drops the old socket record and starts
     * the replacement back in `provisional` state.
     */
    registerConnection(selector: ConnectionSelector, connectParams: ConnectParams, auth?: Auth): Readonly<ConnectionContext>;
    /**
     * Backfills `initConnection` data for sockets that were registered before the
     * client could send its full init payload.
     *
     * This updates metadata only; it does not validate the connection.
     */
    initConnection(selector: ConnectionSelector, body: InitConnectionBody): Readonly<ConnectionContext>;
    /**
     * A material auth change demotes the connection back to provisional until it
     * is validated again.
     */
    updateAuth(selector: ConnectionSelector, body: UpdateAuthBody): Promise<Readonly<ConnectionContext>>;
    /**
     * Validates one connection against the group's pinned `userID`.
     *
     * The first successful validation binds the group `userID`. Later
     * validations must match it. Validation also refreshes the connection's
     * revalidation deadline and may pick the connection as the group
     * background connection if none is currently available. If the websocket is
     * gone by the time async validation finishes, this becomes a no-op.
     */
    validateConnection(selector: ConnectionSelector, revision: number): Readonly<{
        connection: ConnectionContext;
        group: GroupAuthState;
    }> | undefined;
    /** Removes one connection due to failed auth and updates all derived background/deadline state. */
    failConnection(selector: ConnectionSelector, revision: number): ConnectionContext | undefined;
    /** Removes one disconnected connection and updates all derived background/deadline state. */
    closeConnection(selector: ConnectionSelector): ConnectionContext | undefined;
    /**
     * Records a successful background retransform. This starts a fresh interval
     * from the manager clock when shared retransform is schedulable, or
     * clears the deadline if it is not.
     */
    markBackgroundRetransformSuccess(selector: ConnectionSelector, revision: number): void;
    deferMaintenance(kind: 'revalidate' | 'retransform'): void;
    /** Returns the current live record for a client slot, if any. */
    getConnectionContext(selector: ConnectionSelector): Readonly<ConnectionContext> | undefined;
    /** Returns the live record for one websocket or throws if it is unavailable. */
    mustGetConnectionContext(selector: ConnectionSelector): Readonly<ConnectionContext>;
    /** Returns the current background connection, if one exists. */
    getBackgroundConnectionContext(): Readonly<ConnectionContext> | undefined;
    mustGetBackgroundConnectionContext(): Readonly<ConnectionContext>;
    /** Returns the shared group auth state. */
    getGroupState(): Readonly<GroupAuthState>;
    /**
     * Reports which maintenance work is currently due.
     *
     * The result is a pure snapshot: callers decide which actions to run and
     * when to wake up next. `earliestDeadlineAt` is the earliest outstanding
     * maintenance deadline, including overdue work, unless a transient failure
     * has deferred all scheduled maintenance until `maintenanceNotBeforeAt`.
     */
    planMaintenance(): {
        dueRevalidations: Readonly<ConnectionContext>[];
        dueRetransform: boolean;
        earliestDeadlineAt: number | undefined;
    };
}
export {};
//# sourceMappingURL=connection-context-manager.d.ts.map