import { EventEmitter } from 'node:events'; import type { Socket } from 'node:net'; import type { ClientConfig } from "./client.type.js"; /** One node of the cluster, as a redial candidate. */ export type Endpoint = { host: string; port: number; }; /** * Manages the low-level TCP/TLS connection to the Iggy server. * Handles connection lifecycle, reconnection, and data buffering. */ export declare class IggyConnection extends EventEmitter { /** Client configuration */ config: ClientConfig; /** Underlying socket connection */ socket: Socket; /** Whether the connection is established */ connected: boolean; /** Whether a connection attempt is in progress */ connecting: boolean; /** Whether the connection is being intentionally closed */ ending: boolean; /** * Whether the socket is being replaced by a deliberate leader redirect * rather than lost. The drop looks the same from the outside, but nothing a * caller submitted is in doubt: work waiting to be sent belongs on the node * the client moves to, not in an error. */ redirecting: boolean; /** Reconnection configuration */ private reconnectOption; /** * Number of passes made over the known endpoints. One pass dials the * endpoint the client is on, the endpoint it was configured with, and every * node the roster named. */ private reconnectCount; /** Shared promise for concurrent callers waiting on one connection attempt */ private connectPromise?; /** Shared promise for callers waiting on automatic reconnection */ private reconnectPromise?; /** Endpoint the client was configured with, kept across leader redirects */ private readonly seedOptions; /** * Every node the roster named on the last read, kept as redial candidates. * A node dies together with its address, and the roster is unreachable * exactly when it is needed, so it has to have been remembered while the * connection was still healthy. */ private rosterEndpoints; /** Incremental response frame decoder */ private responseDecoder; /** * Creates a new IggyConnection. * * @param config - Client configuration */ constructor(config: ClientConfig); /** * Attaches the lifecycle listeners exactly once per socket instance. * Attaching them in `connect()` would stack duplicate handlers whenever a * failed attempt is retried on the same socket. */ private _installSocket; /** * Establishes the connection to the server. * * @returns Promise that resolves when connected */ connect(boundDial?: boolean): Promise; /** * Waits for one dial, bounded while other endpoints are queued behind it. * * A socket has no connect deadline of its own and there is no 'timeout' * listener on it, so a node whose syns are dropped holds the pass for the * whole OS connect timeout -- and it leads every pass, because the current * endpoint only moves on success. The bound covers the TLS handshake too, * which is what `_readyEvent()` waits for and has no deadline of its own * either. It matches the Rust, Go and C# SDKs'. */ private _dialWithin; /** * The event that says a socket can carry a request. * * On TLS that is 'secureConnect', not 'connect': the latter fires as soon as * the TCP handshake completes, so waiting on it would treat a peer that * never answers the ClientHello as connected and leave the handshake with no * deadline at all. */ private _readyEvent; private _waitForConnection; /** * Attempts to reconnect to the server. * Respects maxRetries limit and emits error when exceeded. * * @param err - Optional error that triggered the reconnection */ reconnect(err?: Error): Promise; private _reconnectUntilConnected; /** * Records the cluster roster as redial candidates. * * Replaced wholesale rather than merged: the roster is the cluster's own * answer about where its nodes are, so a node it dropped stops being * dialed. The configured seed is kept separately and outlives it. */ rememberRoster(endpoints: Endpoint[]): void; /** * The roster endpoint after the one this connection is on, for a request * the current node keeps refusing to admit. Metadata and partition * consensus groups elect independently, so the metadata leader can hold a * follower replica of the partition a request targets, and only walking * the roster reaches that group's primary. `undefined` when the roster * names nowhere else to go. */ nextRosterEndpoint(visited: Set): Endpoint | undefined; /** * Endpoints a redial rotates through, likeliest first: where the client * currently is, the endpoint it was configured with, then the roster it * learned while connected. After a leader redirect the current endpoint may * die with the leader, and the rest of the list is the way back to the * cluster. * * Duplicates are dropped by spelling: the loopback aliases and an * IPv4-mapped IPv6 address collapse onto one endpoint. Names are not * resolved, so a seed given as a DNS name and the roster's IP for the same * node still count as two candidates -- one wasted dial per pass, not a * correctness problem. */ _redialCandidates(): ClientConfig['options'][]; redirect(host: string, port: number): Promise; abort(): void; isConnectedTo(host: string, port: number): boolean; /** * Destroys the connection and marks it as ending. */ _destroy(): void; /** * Clears the response buffer and resets the waiting state. */ _endResponseWait(): void; /** * Handles incoming data from the socket. * Buffers incomplete responses and emits complete ones. * * @param data - Incoming data buffer */ _onData(data: Buffer): void; writeFrame(frame: Buffer): void; } export declare const endpointKey: (endpoint: Endpoint) => string; //# sourceMappingURL=client.connection.d.ts.map