import { EventEmitter } from "node:events";
export interface RspInfo {
    errorId: number;
    errorMsg: string;
}
export interface CallbackOptions {
    /** Request id this record answers (for req/rsp correlation). */
    requestId?: number;
    /** Whether this is the last record of the response. */
    isLast?: boolean;
    /** Present only when CTP reported an error (errorId !== 0). */
    rspInfo?: RspInfo;
}
export interface NativeClient {
    _start(drain: () => void): void;
    _buffer(): ArrayBuffer;
    _claim(): number;
    _release(n: number): void;
    _info(): {
        slotSize: number;
        numSlots: number;
        headerSize: number;
        dropOldest: boolean;
    };
    _dropCount(): number;
    _readIndex?(): number;
    _validate?(claimed: number): number;
    close(): void;
}
export declare abstract class CtpClient extends EventEmitter {
    protected readonly native: any;
    private readonly layouts;
    private readonly decoders;
    private readonly events;
    private view;
    private u8;
    private slotSize;
    private numSlots;
    private headerSize;
    private readPos;
    private dropOldest;
    private batch;
    private closing;
    private started;
    private readonly pending;
    private reqSeq;
    /** Event ids whose requestId-0 response should resolve the oldest pending
     *  request. Needed only for SimNow market-data login/logout, which don't echo
     *  the id; scoping to those events means an unrelated id-0 response
     *  (sub/unsub/error) can't hijack them. The trader echoes ids and leaves this
     *  empty. */
    protected readonly zeroIdFallbackEvents: Set<number>;
    /** A request that goes idle this long (no response and, for multi-row queries,
     *  no further rows) rejects, so awaiters don't hang forever. The timer resets
     *  on each row, so a large streaming query isn't cut off by total duration. */
    protected requestTimeoutMs: number;
    protected constructor(native: any, layoutBlob: Int32Array, events: Map<number, string>);
    /** Reject and clear every in-flight request (on disconnect / close). */
    private rejectAllPending;
    /** Wire up the ring view + doorbell. Subclasses call this in their ctor. */
    protected start(): void;
    /** Records dropped under backpressure (ring full). Monitor this. */
    get droppedRecords(): number;
    close(): void;
    /** Re-surface an exception thrown by a user event handler without letting it
     *  break the drain loop. By the time this runs (next tick) the ring has
     *  already advanced, so the feed never wedges. If the app subscribes to
     *  'error' the exception is routed there (catchable); otherwise it is rethrown
     *  as an uncaughtException — the normal fate of a throwing EventEmitter
     *  listener, just deferred so the data plane stays consistent. */
    private surfaceHandlerError;
    private decoderFor;
    private drain;
    /**
     * Drop-oldest drain (market data). The producer overwrites the oldest unread
     * record when full and never blocks, so the consumer can be lapped mid-read.
     * We therefore decode the whole claimed batch first, then ask the channel how
     * many of the oldest records it overwrote *during* that decode (validate()),
     * discard those (they hold torn bytes), and only then deliver the rest — so a
     * torn record never reaches a handler. The records we do deliver are always the
     * freshest available, and conservation holds (received + dropped == produced).
     */
    private drainOldest;
    private dispatch;
    /** Decode one slot into a record (pure read of the ring; no side effects). */
    private decodeSlot;
    /** Settle any matching in-flight request, then emit the named event. Side
     *  effects only — reads no ring memory, so it is safe to call after the
     *  producer may have advanced (drainOldest decodes before this runs). */
    private deliver;
    /** Encode a plain object into a struct's raw bytes (for req* encoders). */
    protected encode(structId: number, obj: Record<string, unknown>): Uint8Array;
    protected nextRequestId(): number;
    /** Message for a non-zero send code. Risk sentinels get a specific message;
     *  the generic pre-trade-risk block also appends the C++ reason (e.g. which
     *  limit) so logs say *why*. Returns undefined for non-risk codes. */
    private riskRejectMessage;
    /**
     * Send a request and resolve when its response arrives (matched by id).
     * `single` resolves with the one row; otherwise with the accumulated rows.
     */
    protected request<T>(send: (id: number) => number, single?: boolean): Promise<T>;
    /** (Re)arm a pending request's idle timeout. Called at send and reset on each
     *  row, so a long streaming query isn't cut off by total elapsed time. */
    private armRequestTimer;
    /**
     * Submit a fire-and-forget order/action. Exchange-bound inserts get no
     * success response from CTP (only a rejection), so this resolves as soon as
     * CTP accepts the send and rejects if the send is refused. Track the order's
     * actual lifecycle (accepted / filled / cancelled) via the rtn-order and
     * rtn-trade events, correlating by orderRef.
     */
    protected submit(send: (id: number) => number): Promise<void>;
}
//# sourceMappingURL=client.d.ts.map