import { EventEmitter } from "node:events";
import { CenterWorkerWsMessage } from "dp-shared";
/**
 * `Omit<T, K>` on a discriminated union collapses the union (TS doesn't
 * distribute by default), which makes the resulting `payload` an
 * intersection of every variant's payload — i.e. effectively `never`.
 * `DistributiveOmit` re-runs Omit per variant.
 */
type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
/** Outgoing message — every CenterWorkerWsMessage variant minus the auto-filled fields. */
export type OutgoingCenterMessage = DistributiveOmit<CenterWorkerWsMessage, "id" | "ts"> & {
    id?: string;
    ts?: number;
};
import { type CenterBootstrapExtras, type CenterConfigOpts, type ParsedCenterBootstrap } from "../config/center-config.js";
/**
 * Thin REST + WS client the worker uses to talk to dp-center.
 *
 * - `bootstrap()`  pulls the full config + optional session blob (REST).
 * - `connect()`    opens the long-lived WS to the WorkerConnection DO; reconnects
 *                  with exponential backoff. Parsed messages emit on the
 *                  client's EventEmitter via the message `type` discriminator.
 * - `send()`       writes a message; if it expects an ack, returns a promise
 *                  that resolves on the matching `ack { ref_id }` or rejects
 *                  on timeout.
 *
 * Network deps (fetch, WS constructor) are injectable for tests so the
 * unit suite doesn't need a live center.
 */
export type CenterClientEventMap = {
    /** Fired once the WS is open and ready to receive. */
    open: [];
    /** Fired on every parsed incoming message; payload is the discriminated union from dp-shared. */
    message: [import("dp-shared").CenterWorkerWsMessage];
    /** Fired when the underlying socket closes (any reason). */
    close: [{
        code: number;
        reason: string;
        wasClean: boolean;
    }];
    /** Fired on unrecoverable error inside the client (parse error, fetch failure). */
    error: [Error];
};
/** Minimal abstract WebSocket the client uses — both `ws.WebSocket` and the global one satisfy this. */
export interface CenterWebSocket {
    readyState: number;
    send(data: string): void;
    close(code?: number, reason?: string): void;
    addEventListener(type: "open" | "message" | "close" | "error", listener: (event: unknown) => void): void;
}
export declare class DuplicateWorkerConnectionError extends Error {
    readonly code = "worker_already_connected";
    readonly status: number;
    constructor(status: number, body: string);
}
export interface CenterClientDeps {
    /** Defaults to `globalThis.fetch`. */
    fetchImpl?: typeof fetch;
    /** Defaults to a factory that returns a new `ws.WebSocket`. */
    wsFactory?: (url: string) => CenterWebSocket;
    /** Defaults to console — tests pass a silent logger. */
    logger?: {
        info: (msg: string) => void;
        warn: (msg: string) => void;
        error: (msg: string) => void;
    };
}
export interface CenterClientOpts extends CenterConfigOpts {
    centerUrl: string;
    workerKey: string;
    /** Override how long send() waits for an ack before rejecting. */
    ackTimeoutMs?: number;
    /** Initial/maximum WS reconnect delays. */
    reconnectMinMs?: number;
    reconnectMaxMs?: number;
}
export declare class CenterClient extends EventEmitter {
    private readonly centerUrl;
    private readonly workerKey;
    private readonly instanceId;
    private readonly workspace;
    private readonly headless?;
    private readonly fetchImpl;
    private readonly wsFactory;
    private readonly logger;
    private readonly ackTimeoutMs;
    private readonly reconnectMinMs;
    private readonly reconnectMaxMs;
    private ws;
    private pendingAcks;
    private reconnectAttempt;
    private reconnectTimer;
    private shuttingDown;
    /** Most recent bootstrap result — used by center-frontend for config_etag comparisons. */
    private lastBootstrap;
    constructor(opts: CenterClientOpts, deps?: CenterClientDeps);
    /** REST: pull the operational config from center. Throws on auth or schema failure. */
    bootstrap(): Promise<ParsedCenterBootstrap>;
    /** Returns the most recent bootstrap result, or null if bootstrap() hasn't been called yet. */
    getLastBootstrap(): ParsedCenterBootstrap | null;
    /** Returns the most recent bootstrap's center-only extras (etag, social accounts, session blob). */
    getExtras(): CenterBootstrapExtras | null;
    /** Open the WS to the WorkerConnection DO. Fire-and-forget; events emit on the EventEmitter. */
    connect(): void;
    /** Open the WS and resolve only after the center accepts the connection lease. */
    connectAndWait(timeoutMs?: number): Promise<void>;
    private openSocket;
    private scheduleReconnect;
    private installUnexpectedResponseHandler;
    private handleIncoming;
    private handleAck;
    private failAllPendingAcks;
    /**
     * Send an envelope. The id is generated if missing. When `expectAck=true`,
     * returns a promise that resolves on the matching ack or rejects after
     * `ackTimeoutMs` with a timeout error.
     */
    send(msg: OutgoingCenterMessage, expectAck?: boolean): Promise<void>;
    close(): Promise<void>;
    private emitError;
    private httpUrl;
    private wsUrl;
}
export {};
