UNPKG

kubemq-js

Version:

KubeMQ SDK for Node.js — gRPC-based messaging client for Events, Queues, Commands, and Queries

1,366 lines (1,354 loc) 106 kB
/** * Machine-readable error codes identifying the specific failure condition. * * @remarks * Every {@link KubeMQError} carries an `ErrorCode`. Use these constants in * `switch` / `if` checks rather than comparing error message strings. * * @see {@link KubeMQError.code} * @see {@link ErrorCategory} */ declare const ErrorCode: { /** The initial connection or a reconnection attempt timed out. */ readonly ConnectionTimeout: "CONNECTION_TIMEOUT"; /** Authentication credentials were rejected by the server. */ readonly AuthFailed: "AUTH_FAILED"; /** A message or request failed input validation before being sent. */ readonly ValidationFailed: "VALIDATION_FAILED"; /** The server is temporarily unavailable (transient). */ readonly Unavailable: "UNAVAILABLE"; /** An operation exceeded its deadline. */ readonly Timeout: "TIMEOUT"; /** The server throttled the request due to rate limiting. */ readonly Throttled: "THROTTLED"; /** The requested channel or resource does not exist. */ readonly NotFound: "NOT_FOUND"; /** The authenticated identity lacks permission for this operation. */ readonly PermissionDenied: "PERMISSION_DENIED"; /** An unrecoverable internal error. */ readonly Fatal: "FATAL"; /** The operation was cancelled via `AbortSignal`. */ readonly Cancelled: "CANCELLED"; /** The reconnect buffer is full and cannot accept more messages. */ readonly BufferFull: "BUFFER_FULL"; /** A streaming connection broke mid-flight. */ readonly StreamBroken: "STREAM_BROKEN"; /** The client has been closed; no further operations are allowed. */ readonly ClientClosed: "CLIENT_CLOSED"; /** The requested feature is not implemented in this SDK version. */ readonly NotImplemented: "NOT_IMPLEMENTED"; /** A client configuration value is invalid. */ readonly ConfigurationError: "CONFIGURATION_ERROR"; /** The transport connection is not in a ready state. */ readonly ConnectionNotReady: "CONNECTION_NOT_READY"; /** All retry attempts have been exhausted. */ readonly RetryExhausted: "RETRY_EXHAUSTED"; }; /** @see {@link ErrorCode} */ type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode]; /** * Broad error categories for high-level error handling strategies. * * @remarks * While {@link ErrorCode} identifies the specific failure, `ErrorCategory` * groups errors by recovery strategy: * - **Transient / Timeout / Throttling** — safe to retry with backoff. * - **Authentication / Authorization** — fix credentials or permissions. * - **Validation / Configuration** — fix the input or config, then retry. * - **Fatal / Cancellation** — not recoverable by retry. * - **Backpressure** — slow down or increase buffer capacity. * - **NotFound** — the target channel or resource doesn't exist. * * @see {@link KubeMQError.category} * @see {@link ErrorCode} */ declare const ErrorCategory: { /** A temporary failure that may self-resolve (e.g. network blip). */ readonly Transient: "Transient"; /** An operation exceeded its deadline. */ readonly Timeout: "Timeout"; /** The server is rate-limiting the client. */ readonly Throttling: "Throttling"; /** Credentials are invalid or expired. */ readonly Authentication: "Authentication"; /** The identity lacks required permissions. */ readonly Authorization: "Authorization"; /** Input did not pass validation rules. */ readonly Validation: "Validation"; /** The target resource was not found. */ readonly NotFound: "NotFound"; /** Unrecoverable failure. */ readonly Fatal: "Fatal"; /** The operation was explicitly cancelled. */ readonly Cancellation: "Cancellation"; /** The system is applying backpressure (buffer full). */ readonly Backpressure: "Backpressure"; /** A configuration parameter is invalid. */ readonly Configuration: "Configuration"; }; /** @see {@link ErrorCategory} */ type ErrorCategory = (typeof ErrorCategory)[keyof typeof ErrorCategory]; /** * Construction options shared by all {@link KubeMQError} subclasses. * * @see {@link KubeMQError} */ interface KubeMQErrorOptions { /** Machine-readable error code. Defaults to {@link ErrorCode.Fatal}. */ code?: ErrorCode; /** Human-readable error description. */ message: string; /** The SDK operation that failed (e.g. `'sendEvent'`). */ operation: string; /** The channel involved, if any. */ channel?: string; /** Whether the operation is safe to retry. */ isRetryable?: boolean; /** The underlying cause, if this error wraps another. */ cause?: Error; /** Correlation ID for tracing. Auto-generated UUID if omitted. */ requestId?: string; /** gRPC status code, when the error originates from the server. */ statusCode?: number; /** Address of the KubeMQ server that returned the error. */ serverAddress?: string; /** Actionable suggestion for resolving the error. */ suggestion?: string; /** Number of retry attempts made before this error was raised. */ retryAttempts?: number; /** Maximum retries configured in the active {@link RetryPolicy}. */ maxRetries?: number; /** Total wall-clock time spent retrying, in milliseconds. */ retryDuration?: number; } /** * Construction options for {@link StreamBrokenError}. * * @see {@link StreamBrokenError} */ interface StreamBrokenErrorOptions extends KubeMQErrorOptions { /** IDs of messages that were sent but not acknowledged before the stream broke. */ unacknowledgedMessageIds: string[]; } /** * Construction options for {@link RetryExhaustedError}. * * @see {@link RetryExhaustedError} */ interface RetryExhaustedErrorOptions extends KubeMQErrorOptions { /** Total number of attempts made (initial + retries). */ attempts: number; /** Total wall-clock time spent across all retry attempts, in milliseconds. */ totalDuration: number; /** The error from the final failed attempt. */ lastError: Error; } /** * Construction options for {@link PartialFailureError}. * * @see {@link PartialFailureError} */ interface PartialFailureErrorOptions extends KubeMQErrorOptions { /** Per-item failures with their batch index and error. */ failures: { index: number; error: KubeMQError; }[]; } /** * Base error class for all KubeMQ SDK errors. * * @remarks * All errors thrown by the SDK are instances of `KubeMQError` (or a subclass). * Use `instanceof KubeMQError` for broad catches, or check specific subclasses * for targeted handling. Cross-version `instanceof` safety is provided via * `Symbol.hasInstance`. * * Key properties for error handling: * - {@link KubeMQError.code | code} — machine-readable {@link ErrorCode} * - {@link KubeMQError.category | category} — broad {@link ErrorCategory} for strategy selection * - {@link KubeMQError.isRetryable | isRetryable} — whether automatic retry is appropriate * - {@link KubeMQError.suggestion | suggestion} — actionable fix hint * * @see {@link ErrorCode} * @see {@link ErrorCategory} */ declare class KubeMQError extends Error { /** * Cross-version instanceof check via well-known symbol. * Only used on the base class — subclass discrimination uses the * standard prototype chain (preserved by Object.setPrototypeOf). */ static [Symbol.hasInstance](instance: unknown): boolean; name: string; readonly code: ErrorCode; readonly operation: string; readonly channel: string | undefined; readonly isRetryable: boolean; readonly requestId: string; readonly statusCode: number | undefined; readonly serverAddress: string | undefined; readonly timestamp: Date; readonly category: ErrorCategory; readonly suggestion: string | undefined; constructor(options: KubeMQErrorOptions); toJSON(): Record<string, unknown>; toSanitizedString(): string; } /** * Thrown when the SDK cannot establish or maintain a connection to the server. * * @remarks * Retryable by default. The reconnection policy handles automatic recovery; * this error surfaces only if reconnection is disabled or exhausted. * * @see {@link KubeMQError} * @see {@link ConnectionNotReadyError} */ declare class ConnectionError extends KubeMQError { readonly category: "Transient"; constructor(options: KubeMQErrorOptions); } /** * Thrown when the server rejects the provided authentication credentials. * * @remarks * Not retryable. Verify the token or {@link CredentialProvider} configuration. * * @see {@link KubeMQError} * @see {@link AuthorizationError} */ declare class AuthenticationError extends KubeMQError { readonly category: "Authentication"; constructor(options: KubeMQErrorOptions); } /** * Thrown when the authenticated identity lacks permission for the requested operation. * * @remarks * Not retryable. The credentials are valid but the associated role/policy * does not grant access to the target channel or operation. * * @see {@link KubeMQError} * @see {@link AuthenticationError} */ declare class AuthorizationError extends KubeMQError { readonly category: "Authorization"; constructor(options: KubeMQErrorOptions); } /** * Thrown when an operation exceeds its configured timeout. * * @remarks * Retryable by default. Consider increasing the timeout via * {@link OperationOptions.timeout} or the relevant default in {@link ClientOptions}. * * @see {@link KubeMQError} */ declare class KubeMQTimeoutError extends KubeMQError { readonly category: "Timeout"; constructor(options: KubeMQErrorOptions); } /** * Thrown when a message or request fails client-side validation before sending. * * @remarks * Not retryable. Fix the invalid input (e.g. empty channel name, missing body) * and re-submit. The `suggestion` property usually indicates what to fix. * * @see {@link KubeMQError} */ declare class ValidationError extends KubeMQError { readonly category: "Validation"; constructor(options: KubeMQErrorOptions); } /** * Thrown for temporary server-side failures that are expected to self-resolve. * * @remarks * Retryable by default. The SDK's built-in retry policy handles these * automatically; this error surfaces only when retries are exhausted. * * @see {@link KubeMQError} * @see {@link RetryPolicy} */ declare class TransientError extends KubeMQError { readonly category: "Transient"; constructor(options: KubeMQErrorOptions); } /** * Thrown when the server rate-limits the client. * * @remarks * Retryable by default with backoff. Reduce request rate or increase * server-side rate limits. * * @see {@link KubeMQError} */ declare class ThrottlingError extends KubeMQError { readonly category: "Throttling"; constructor(options: KubeMQErrorOptions); } /** * Thrown when the target channel or resource does not exist on the server. * * @remarks * Not retryable. Create the channel first via * {@link KubeMQClient.createChannel} or one of the convenience aliases. * * @see {@link KubeMQError} */ declare class NotFoundError extends KubeMQError { readonly category: "NotFound"; constructor(options: KubeMQErrorOptions); } /** * Thrown for unrecoverable internal failures. * * @remarks * Not retryable by default. Indicates a server-side or SDK-internal bug. * Report to the KubeMQ team if recurring. * * @see {@link KubeMQError} */ declare class FatalError extends KubeMQError { readonly category: "Fatal"; constructor(options: KubeMQErrorOptions); } /** * Thrown when an operation is cancelled via `AbortSignal`. * * @remarks * Not retryable. This is the expected error when cooperative cancellation * is triggered by the caller through {@link OperationOptions.signal}. * * @see {@link KubeMQError} */ declare class CancellationError extends KubeMQError { readonly category: "Cancellation"; constructor(options: KubeMQErrorOptions); } /** * Thrown when the reconnect buffer is full and cannot accept more messages. * * @remarks * Not retryable. Increase {@link ClientOptions.reconnectBufferSize} or * switch `reconnectBufferMode` to `'block'` for flow control. * * @see {@link KubeMQError} * @see {@link ClientOptions.reconnectBufferSize} */ declare class BufferFullError extends KubeMQError { readonly category: "Backpressure"; constructor(options: KubeMQErrorOptions); } /** * Thrown when a streaming connection (subscription or queue stream) breaks mid-flight. * * @remarks * Retryable by default. Carries `unacknowledgedMessageIds` — the IDs of messages * that were sent but not yet acknowledged at the time the stream broke. * Application code should decide whether to re-send or deduplicate these messages. * * @see {@link KubeMQError} * @see {@link StreamBrokenErrorOptions} */ declare class StreamBrokenError extends KubeMQError { readonly category: "Transient"; readonly unacknowledgedMessageIds: string[]; constructor(options: StreamBrokenErrorOptions); } /** * Thrown when an operation is attempted on a client that has already been closed. * * @remarks * Not retryable. Create a new {@link KubeMQClient} instance if further * operations are needed. * * @see {@link KubeMQError} * @see {@link KubeMQClient.close} */ declare class ClientClosedError extends KubeMQError { readonly category: "Fatal"; constructor(options: KubeMQErrorOptions); } /** * Thrown when an operation is attempted before the transport connection is ready. * * @remarks * Not retryable by default. Usually indicates the client is still connecting * or in the process of reconnecting. Wait for the `'connected'` or * `'reconnected'` event before retrying. * * @see {@link ConnectionError} * @see {@link ConnectionState} */ declare class ConnectionNotReadyError extends ConnectionError { readonly category: "Transient"; constructor(options: KubeMQErrorOptions); } /** * Thrown when {@link ClientOptions} contain invalid or conflicting values. * * @remarks * Not retryable. Fix the configuration and create a new client. * * @see {@link KubeMQError} * @see {@link ClientOptions} */ declare class ConfigurationError extends KubeMQError { readonly category: "Configuration"; constructor(options: KubeMQErrorOptions); } /** * Thrown when all retry attempts for an operation have been exhausted. * * @remarks * Not retryable. Carries detailed retry diagnostics: `attempts`, `totalDuration`, * and `lastError` (the error from the final attempt). Consider increasing * {@link RetryPolicy.maxRetries} or investigating the root cause via `lastError`. * * @see {@link KubeMQError} * @see {@link RetryPolicy} * @see {@link RetryExhaustedErrorOptions} */ declare class RetryExhaustedError extends KubeMQError { readonly attempts: number; readonly maxRetries: number; readonly totalDuration: number; readonly lastError: Error; constructor(options: RetryExhaustedErrorOptions); toSanitizedString(): string; } /** * Thrown when a requested feature is not implemented in this SDK version. * * @remarks * Not retryable. Check the SDK release notes for feature availability * or use an alternative API surface. * * @see {@link KubeMQError} */ declare class NotImplementedError extends KubeMQError { readonly category: "Fatal"; constructor(options: KubeMQErrorOptions); } /** * Thrown when a batch operation partially succeeds — some items failed while others succeeded. * * @remarks * Inspect the {@link PartialFailureError.failures | failures} array for per-item * error details including the batch `index` and the associated {@link KubeMQError}. * * @see {@link KubeMQError} * @see {@link PartialFailureErrorOptions} * @see {@link KubeMQClient.sendQueueMessagesBatch} */ declare class PartialFailureError extends KubeMQError { readonly failures: { index: number; error: KubeMQError; }[]; constructor(options: PartialFailureErrorOptions); } /** * Thrown when a user-provided callback or handler throws an unhandled error. * * @remarks * Not retryable by default. Wraps the original error thrown by the user's * subscription callback or event handler. Fix the handler code to prevent * unhandled exceptions. * * @see {@link KubeMQError} */ declare class HandlerError extends KubeMQError { readonly category: "Fatal"; constructor(options: KubeMQErrorOptions); } /** * Connection lifecycle states. * * Transition diagram: * IDLE ──> CONNECTING ──> READY * ^ │ │ * │ v v * │ RECONNECTING ──> READY * │ │ ↺ (self) * v v * CLOSED (terminal) * * RECONNECTING → RECONNECTING is a valid self-transition representing * a new reconnection attempt. The 'reconnecting' event fires on each attempt. * * @internal */ declare enum ConnectionState { IDLE = "IDLE", CONNECTING = "CONNECTING", READY = "READY", RECONNECTING = "RECONNECTING", CLOSED = "CLOSED" } /** @internal — not part of public API */ interface TransportCallOptions { deadline?: Date; signal?: AbortSignal; } interface StreamHandle<TWrite, TRead> { write(msg: TWrite): boolean; onData(handler: (msg: TRead) => void): void; onError(handler: (err: Error) => void): void; onEnd(handler: () => void): void; cancel(): void; end(): void; /** Pause the readable side of the stream (C3 backpressure). No-op if not supported. */ pause(): void; /** Resume the readable side of the stream (C3 backpressure). No-op if not supported. */ resume(): void; /** Remove all listeners from the underlying stream (H2 rebind cleanup). */ removeAllListeners(): void; /** Register a one-shot handler for when the write buffer drains (writable-side backpressure). */ onDrain(handler: () => void): void; } interface Transport { readonly state: ConnectionState; connect(): Promise<void>; close(timeoutMs?: number): Promise<void>; unaryCall<TReq, TRes>(method: string, request: TReq, options?: TransportCallOptions): Promise<TRes>; serverStream<TReq, TRes>(method: string, request: TReq, options?: TransportCallOptions): StreamHandle<never, TRes>; duplexStream<TReq, TRes>(method: string, options?: TransportCallOptions): StreamHandle<TReq, TRes>; getMetadata(): Record<string, string>; setMetadata(key: string, value: string): void; on(event: 'stateChange', handler: (state: ConnectionState) => void): void; off(event: 'stateChange', handler: (state: ConnectionState) => void): void; } /** * Structured logging interface. Users inject their preferred logger * (pino, winston, bunyan, etc.) via ClientOptions. * * Default: noopLogger — zero output unless configured. */ interface Logger { debug(msg: string, fields?: Record<string, unknown>): void; info(msg: string, fields?: Record<string, unknown>): void; warn(msg: string, fields?: Record<string, unknown>): void; error(msg: string, fields?: Record<string, unknown>): void; } /** Structured key-value context attached to log entries. */ type LogContext = Record<string, unknown>; /** Log severity levels. 'off' disables all output. */ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off'; /** * Default no-op logger. All methods are empty — zero overhead, * zero output. Users replace this via ClientOptions.logger. */ declare const noopLogger: Logger; /** * Creates a console-based logger filtered by level. * Intended for development/debugging — NOT used internally by the SDK. * * @example * ```ts * import { createConsoleLogger } from 'kubemq-js'; * * const client = await KubeMQClient.create({ * address: 'localhost:50000', * logger: createConsoleLogger('debug'), * }); * ``` */ declare function createConsoleLogger(level: LogLevel): Logger; /** * @internal — Abstract base class for shared bidi streaming senders. * * Provides: bounded queue, setImmediate-based drain loop with write * backpressure, transport state integration (no self-reconnect), * deadline sweep, AbortSignal support, error handler fan-out, and * observability stats. * * Subclasses implement openStream(), extractPendingKey(), handleResponse(). */ type SenderStreamState = 'initializing' | 'connected' | 'reconnecting' | 'closed'; interface SenderStats { queueDepth: number; pendingAcks: number; streamState: SenderStreamState; reconnectionCount: number; } /** * Pluggable credential provider interface for authentication. * * Implementations must be safe for concurrent invocation from * the event loop (the SDK serializes calls, but user code may not). * * The SDK caches the returned token and re-invokes the provider only when: * - No cached token exists * - The cached token is invalidated by a server UNAUTHENTICATED response * - Proactive refresh determines the token is approaching expiry * * At most one outstanding getToken() call is in flight at any time. */ interface CredentialProvider { getToken(): Promise<{ token: string; expiresAt?: Date; }>; } /** * Simple credential provider that returns a fixed authentication token. * * @remarks * Suitable for development or environments where tokens do not expire. * For production with rotating tokens, implement a custom {@link CredentialProvider}. * * Passing a plain string to {@link ClientOptions.credentials} automatically * wraps it in a `StaticTokenProvider`. * * @see {@link CredentialProvider} * @see {@link ClientOptions.credentials} */ declare class StaticTokenProvider implements CredentialProvider { #private; constructor(token: string); getToken(): Promise<{ token: string; expiresAt?: Date; }>; toString(): string; toJSON(): Record<string, unknown>; } /** * Jitter strategy applied to retry and reconnection backoff delays. * * @remarks * - `'full'` — Uniform random jitter in `[0, computedDelay]`. Best general-purpose choice. * - `'equal'` — Half the computed delay is fixed, the other half is randomized. * - `'none'` — No jitter; uses the raw exponential delay. Risk of thundering-herd. * * @see {@link RetryPolicy} * @see {@link ReconnectionPolicy} */ type JitterType = 'full' | 'equal' | 'none'; /** * Policy governing automatic retries for failed operations. * * @remarks * Applied to all retriable SDK operations (publish, send, queue send, etc.). * The actual delay between retries is computed as * `min(initialBackoffMs * multiplier^attempt, maxBackoffMs)`, then jittered * according to the {@link JitterType} strategy. * * @see {@link DEFAULT_RETRY_POLICY} * @see {@link ClientOptions.retry} */ interface RetryPolicy { /** Maximum number of retry attempts before throwing {@link RetryExhaustedError}. */ readonly maxRetries: number; /** Base delay in milliseconds for the first retry. */ readonly initialBackoffMs: number; /** Upper bound on the computed backoff delay in milliseconds. */ readonly maxBackoffMs: number; /** Exponential multiplier applied to the backoff after each attempt. */ readonly multiplier: number; /** Jitter strategy to reduce retry contention. */ readonly jitter: JitterType; } declare const DEFAULT_RETRY_POLICY: Readonly<RetryPolicy>; declare const DEFAULT_KEEPALIVE: Readonly<KeepaliveOptions>; declare const DEFAULT_RECONNECTION_POLICY: Readonly<ReconnectionPolicy>; declare const DEFAULT_CONNECTION_TIMEOUT_MS = 10000; declare const DEFAULT_MAX_MESSAGE_SIZE = 104857600; declare const DEFAULT_RECONNECT_BUFFER_SIZE = 8388608; declare const DEFAULT_SEND_TIMEOUT_MS = 5000; declare const DEFAULT_SUBSCRIBE_TIMEOUT_MS = 10000; declare const DEFAULT_RPC_TIMEOUT_MS = 10000; declare const DEFAULT_QUEUE_RECEIVE_TIMEOUT_MS = 10000; declare const DEFAULT_QUEUE_POLL_TIMEOUT_MS = 30000; declare const DEFAULT_MAX_CONCURRENT_RETRIES = 10; /** * Policy governing automatic reconnection when the gRPC transport disconnects. * * @remarks * When the connection drops, the client enters `RECONNECTING` state and * attempts to re-establish the connection using exponential backoff with jitter. * Set `maxAttempts` to `-1` for unlimited reconnection attempts. * * Active subscriptions are automatically re-established after a successful * reconnection. Buffered messages (if `reconnectBufferSize > 0`) are replayed. * * @see {@link DEFAULT_RECONNECTION_POLICY} * @see {@link ClientOptions.reconnect} * @see {@link ConnectionState} */ interface ReconnectionPolicy { /** Maximum reconnection attempts. Use `-1` for unlimited. */ readonly maxAttempts: number; /** Base delay in milliseconds for the first reconnection attempt. */ readonly initialDelayMs: number; /** Upper bound on the computed reconnection delay in milliseconds. */ readonly maxDelayMs: number; /** Exponential multiplier applied to the delay after each attempt. */ readonly multiplier: number; /** Jitter strategy to reduce reconnection contention across clients. */ readonly jitter: JitterType; } /** * TLS/SSL configuration for encrypting the gRPC connection. * * @remarks * Set `enabled: true` (or pass `tls: true` in {@link ClientOptions}) for * server-authenticated TLS. For mutual TLS (mTLS), also provide `clientCert` * and `clientKey`. Certificate values accept PEM-encoded strings or raw Buffers. * * @see {@link ClientOptions.tls} */ interface TlsOptions { /** Whether TLS is enabled. Defaults to `false`. */ enabled?: boolean; /** PEM-encoded CA certificate or bundle for server verification. */ caCert?: string | Buffer; /** PEM-encoded client certificate for mutual TLS. */ clientCert?: string | Buffer; /** PEM-encoded client private key for mutual TLS. */ clientKey?: string | Buffer; /** Override the server name used for certificate verification. */ serverNameOverride?: string; /** Skip server certificate verification. **Insecure — use only for development.** */ insecureSkipVerify?: boolean; /** Minimum TLS version to accept. */ minVersion?: 'TLSv1.2' | 'TLSv1.3'; } /** * gRPC HTTP/2 keepalive configuration. * * @remarks * Keepalive pings prevent idle connections from being silently dropped * by firewalls or load balancers. The defaults are tuned for most * cloud environments (10s ping interval, 5s timeout). * * @see {@link DEFAULT_KEEPALIVE} * @see {@link ClientOptions.keepalive} */ interface KeepaliveOptions { /** Interval in milliseconds between keepalive pings. */ readonly timeMs: number; /** Time in milliseconds to wait for a keepalive ping response before closing. */ readonly timeoutMs: number; /** Whether to send pings even when there are no active RPCs. */ readonly permitWithoutCalls: boolean; } /** * Options for individual async operations. * * @remarks * Pass to any async method on `KubeMQClient` to control cancellation * and timeout behavior for that specific operation. */ interface OperationOptions { /** * AbortSignal for cooperative cancellation. * When aborted, the operation is cancelled and throws `CancellationError`. * * @example * ```typescript * const controller = new AbortController(); * setTimeout(() => controller.abort(), 5000); * await client.sendEvent(msg, { signal: controller.signal }); * ``` */ signal?: AbortSignal; /** * Operation timeout in milliseconds. * Overrides the client-level default timeout for this operation. * When exceeded, throws `KubeMQTimeoutError`. */ timeout?: number; } /** * Options for subscription operations, extending OperationOptions. */ interface SubscriptionOptions extends OperationOptions { /** * Maximum number of concurrent callback invocations. * Default varies by subscription type: events=100, events_store=20, commands/queries=1. * Set to a higher value for parallel message processing. * * @remarks * When > 1, messages are dispatched to the callback concurrently * using an internal semaphore. Message ordering is NOT guaranteed * when concurrency > 1. Use 1 for ordered processing. */ maxConcurrentCallbacks?: number; /** * Maximum internal queue depth before backpressure is applied. * Default: 1000. */ maxQueueDepth?: number; /** * If true, drop messages when the internal queue is full instead of * pausing the gRPC stream. Useful for pub/sub patterns where message * loss is acceptable but stream stalls are not. Default: false. */ dropOnHighWater?: boolean; } /** * Options for the `close()` method. */ interface CloseOptions { /** * Max time to wait for in-flight gRPC operations to drain, in seconds. * Default: 5. */ timeoutSeconds?: number; /** * Max time to wait for in-flight subscription callbacks to complete, in seconds. * Default: 30. * * Callbacks that haven't completed within this timeout are abandoned — * they may still be running in the background but the client will * proceed to close. */ callbackTimeoutSeconds?: number; } /** * Configuration options for creating a {@link KubeMQClient}. * * @remarks * Only `address` is required. All other options have sensible defaults. * Pass to {@link KubeMQClient.create} to build a connected client. * * @example * ```typescript * const client = await KubeMQClient.create({ * address: 'localhost:50000', * clientId: 'order-service', * logger: createConsoleLogger('info'), * retry: { ...DEFAULT_RETRY_POLICY, maxRetries: 5 }, * }); * ``` * * @see {@link KubeMQClient.create} */ interface ClientOptions { /** KubeMQ server address in `host:port` format. */ address: string; /** Unique client identifier. Auto-generated UUID if omitted. */ clientId?: string; /** Authentication credentials — a token string or a {@link CredentialProvider}. */ credentials?: CredentialProvider | string; /** TLS configuration. Pass `true` for default TLS, or a {@link TlsOptions} object. */ tls?: TlsOptions | boolean; /** HTTP/2 keepalive settings. Uses {@link DEFAULT_KEEPALIVE} if omitted. */ keepalive?: KeepaliveOptions; /** Retry policy for failed operations. Uses {@link DEFAULT_RETRY_POLICY} if omitted. */ retry?: RetryPolicy; /** Reconnection policy for dropped connections. Uses {@link DEFAULT_RECONNECTION_POLICY} if omitted. */ reconnect?: ReconnectionPolicy; /** Maximum time in seconds to wait for the initial connection. Default: 10. */ connectionTimeoutSeconds?: number; /** Maximum inbound message size in bytes. Default: {@link DEFAULT_MAX_MESSAGE_SIZE} (100 MiB). */ maxReceiveMessageSize?: number; /** Maximum outbound message size in bytes. Default: {@link DEFAULT_MAX_MESSAGE_SIZE} (100 MiB). */ maxSendMessageSize?: number; /** Block until the gRPC channel is ready instead of failing fast. */ waitForReady?: boolean; /** Structured logger implementation. Default: silent no-op logger. */ logger?: Logger; /** OpenTelemetry TracerProvider for distributed tracing. */ tracerProvider?: unknown; /** OpenTelemetry MeterProvider for metrics collection. */ meterProvider?: unknown; /** Size in bytes of the in-memory buffer for messages sent during reconnection. Default: {@link DEFAULT_RECONNECT_BUFFER_SIZE}. */ reconnectBufferSize?: number; /** Behavior when the reconnect buffer is full: `'error'` throws {@link BufferFullError}, `'block'` waits. */ reconnectBufferMode?: 'error' | 'block'; /** Maximum number of concurrent retry operations across all calls. Default: {@link DEFAULT_MAX_CONCURRENT_RETRIES}. */ maxConcurrentRetries?: number; /** Default timeout in seconds for publish/send operations. Default: 5. */ defaultSendTimeoutSeconds?: number; /** Default timeout in seconds for subscribe operations. Default: 10. */ defaultSubscribeTimeoutSeconds?: number; /** Default timeout in seconds for RPC (command/query) operations. Default: 10. */ defaultRpcTimeoutSeconds?: number; /** Default timeout in seconds for queue receive operations. Default: 10. */ defaultQueueReceiveTimeoutSeconds?: number; /** Default timeout in seconds for queue poll operations. Default: 30. */ defaultQueuePollTimeoutSeconds?: number; } /** @internal */ /** * Union type for message body inputs. Accepts string (auto-encoded to UTF-8), * Uint8Array, or Node.js Buffer (zero-copy view extracted). * * Public API methods and factory functions accept `MessageBody`; * internally the SDK normalizes to `Uint8Array` before sending to gRPC. */ type MessageBody = string | Uint8Array | Buffer; /** * Normalize any accepted body input to a `Uint8Array`. * * - `string` → UTF-8 encoded via cached `TextEncoder` * - `Buffer` → zero-copy `Uint8Array` view (no data copy) * - `Uint8Array` → returned as-is */ declare function normalizeBody(body: MessageBody): Uint8Array; /** * Decode a `Uint8Array` body to a UTF-8 string. * Uses the cached `TextDecoder` singleton. */ declare function bodyToString(body: Uint8Array): string; /** * Outbound event message. * * @remarks * **Async safety:** Not safe for concurrent modification. Create a new instance * per send operation. Do not share outbound message objects between concurrent * async operations. Message objects are frozen (`Object.freeze()`) by factory * functions — modification after creation throws a `TypeError`. */ interface EventMessage { readonly channel: string; readonly body?: MessageBody; readonly metadata?: string; readonly tags?: Record<string, string>; readonly id?: string; readonly clientId?: string; } /** * Received event from a subscription. * * @remarks * **Async safety:** Safe to read from multiple async contexts concurrently. * Do not modify received message objects — they are shared references from * the subscription's delivery pipeline. Fields are readonly. */ interface EventReceived { readonly id: string; readonly channel: string; readonly timestamp: Date; readonly body: Uint8Array; readonly metadata: string; readonly tags: Record<string, string>; } /** * Subscription request for events. * * @remarks * **Async safety:** Subscription callbacks fire sequentially on the Node.js * event loop by default. Opt-in concurrent processing is available via * `maxConcurrentCallbacks` in `SubscriptionOptions`. When concurrency > 1, * message ordering is NOT guaranteed. */ interface EventsSubscription { readonly channel: string; readonly group?: string; readonly onEvent: (event: EventReceived) => void; readonly onError: (err: KubeMQError) => void; } /** * Handle for a persistent event publishing stream (fire-and-forget). * * @remarks * Obtained from {@link KubeMQClient.createEventStream}. Keeps a single gRPC * bidirectional stream open for high-throughput publishing. The `send()` method * is synchronous (fire-and-forget) — errors are delivered asynchronously via * the `onError` handler. * * @see {@link KubeMQClient.createEventStream} * @see {@link EventMessage} */ interface EventStreamHandle { /** Publish an event over the stream. Returns a Promise that resolves when the write buffer has capacity (backpressure-aware). */ send(msg: EventMessage): Promise<void>; /** Register a handler for asynchronous stream errors. */ onError(handler: (err: Error) => void): void; /** Close the stream and release resources. */ close(): void; /** Whether the stream is still active. */ readonly isActive: boolean; } /** * Create a validated, frozen EventMessage with defaults applied. * * - `id` defaults to a random UUID * - `metadata` defaults to `''` * - `tags` defaults to `{}` * - String/Buffer body is normalized to `Uint8Array` * * @example * ```typescript * const event = createEventMessage({ * channel: 'events.notifications', * body: new TextEncoder().encode('hello world'), * metadata: 'greeting', * tags: { source: 'api' }, * }); * await client.sendEvent(event); * ``` */ declare function createEventMessage(opts: Omit<EventMessage, 'id'> & { id?: string; }): Readonly<EventMessage>; /** * Subscription start position for event-store channels. * * @remarks * Determines where in the persisted event stream a new subscriber begins * receiving messages. Used in {@link EventStoreSubscription.startFrom}. * * @see {@link EventStoreSubscription} * @see {@link KubeMQClient.subscribeToEventsStore} */ declare enum EventStoreStartPosition { /** Receive only events published after the subscription is created. */ StartFromNew = 1, /** Replay all events from the beginning of the stream. */ StartFromFirst = 2, /** Start from the most recent event and receive new ones going forward. */ StartFromLast = 3, /** Start at a specific sequence number (set via {@link EventStoreSubscription.startValue}). */ StartAtSequence = 4, /** Start at a specific point in time (set via {@link EventStoreSubscription.startValue} as Unix epoch ms). */ StartAtTime = 5, /** Start at a time delta in seconds from now (set via {@link EventStoreSubscription.startValue}). */ StartAtTimeDelta = 6 } /** * Outbound persistent event message. * * @remarks * **Async safety:** Not safe for concurrent modification. Create a new instance * per send operation. Do not share outbound message objects between concurrent * async operations. Message objects are frozen (`Object.freeze()`) by factory * functions — modification after creation throws a `TypeError`. */ interface EventStoreMessage { readonly channel: string; readonly body?: MessageBody; readonly metadata?: string; readonly tags?: Record<string, string>; readonly id?: string; readonly clientId?: string; } /** * Received persistent event from a subscription. * * @remarks * **Async safety:** Safe to read from multiple async contexts concurrently. * Do not modify received message objects — they are shared references from * the subscription's delivery pipeline. Fields are readonly. */ interface EventStoreReceived { readonly id: string; readonly channel: string; readonly timestamp: Date; readonly body: Uint8Array; readonly metadata: string; readonly tags: Record<string, string>; readonly sequence: number; } /** * Subscription request for persistent events. * * @remarks * **Async safety:** Subscription callbacks fire sequentially on the Node.js * event loop by default. Opt-in concurrent processing is available via * `maxConcurrentCallbacks` in `SubscriptionOptions`. When concurrency > 1, * message ordering is NOT guaranteed. */ interface EventStoreSubscription { readonly channel: string; readonly group?: string; readonly startFrom: EventStoreStartPosition; readonly startValue?: number; readonly onEvent: (event: EventStoreReceived) => void; readonly onError: (err: KubeMQError) => void; } /** * Result of a persistent event-store send operation. * * @see {@link KubeMQClient.sendEventStore} */ interface EventStoreResult { /** Server-assigned event ID. */ readonly id: string; /** Whether the event was successfully persisted. */ readonly sent: boolean; /** Error message if the send failed. */ readonly error: string; } /** * Handle for a persistent event-store publishing stream with delivery confirmation. * * @remarks * Obtained from {@link KubeMQClient.createEventStoreStream}. Unlike * {@link EventStreamHandle}, the `send()` method returns a `Promise` * that resolves when the server confirms persistence, or rejects on failure. * * @see {@link KubeMQClient.createEventStoreStream} * @see {@link EventStoreMessage} */ interface EventStoreStreamHandle { /** Publish an event-store message. Resolves when the server confirms persistence. */ send(msg: EventStoreMessage): Promise<void>; /** Register a handler for asynchronous stream errors. */ onError(handler: (err: Error) => void): void; /** Close the stream and release resources. */ close(): void; /** Whether the stream is still active. */ readonly isActive: boolean; } /** * Create a validated, frozen EventStoreMessage with defaults applied. * * - `id` defaults to a random UUID * - `metadata` defaults to `''` * - `tags` defaults to `{}` * - String/Buffer body is normalized to `Uint8Array` * * @example * ```typescript * const event = createEventStoreMessage({ * channel: 'events-store.audit-log', * body: new TextEncoder().encode(JSON.stringify({ action: 'login', userId: '42' })), * tags: { service: 'auth' }, * }); * await client.sendEventStore(event); * ``` */ declare function createEventStoreMessage(opts: Omit<EventStoreMessage, 'id'> & { id?: string; }): Readonly<EventStoreMessage>; /** * Delivery policy for a queue message controlling expiration, delay, and dead-letter behavior. * * @remarks * Attach to a {@link QueueMessage} via the `policy` property. * When `maxReceiveCount` is exceeded, the message is routed to `maxReceiveQueue` * (dead-letter queue) if specified, otherwise it is discarded. * * @see {@link QueueMessage} * @see {@link KubeMQClient.sendQueueMessage} */ interface QueueMessagePolicy { /** Time in seconds after which the message expires and is discarded. */ readonly expirationSeconds?: number; /** Delay in seconds before the message becomes visible to consumers. */ readonly delaySeconds?: number; /** Maximum number of delivery attempts before dead-lettering. */ readonly maxReceiveCount?: number; /** Dead-letter queue channel. Messages exceeding `maxReceiveCount` are routed here. */ readonly maxReceiveQueue?: string; } /** * Outbound queue message. * * @remarks * **Async safety:** Not safe for concurrent modification. Create a new instance * per send operation. Do not share outbound message objects between concurrent * async operations. Message objects are frozen (`Object.freeze()`) by factory * functions — modification after creation throws a `TypeError`. */ interface QueueMessage { readonly channel: string; readonly body?: MessageBody; readonly metadata?: string; readonly tags?: Record<string, string>; readonly policy?: QueueMessagePolicy; readonly id?: string; readonly clientId?: string; } /** * Received queue message with acknowledgment methods. * * @remarks * **Async safety:** The `ack()`, `nack()`, and `reQueue()` methods are safe * to call from any async context, but each message MUST be acknowledged exactly * once. Calling `ack()` after `nack()` (or vice versa) throws a * `ValidationError`. */ interface ReceivedQueueMessage { readonly id: string; readonly channel: string; readonly fromClientId: string; readonly body: Uint8Array; readonly metadata: string; readonly tags: Record<string, string>; readonly timestamp: Date; readonly sequence: number; readonly receiveCount: number; readonly isReRouted: boolean; readonly reRouteFromQueue?: string; readonly expiredAt?: Date; readonly delayedTo?: Date; ack(): Promise<void>; nack(): Promise<void>; reQueue(channel: string): Promise<void>; } /** * Request parameters for polling messages from a queue channel. * * @remarks * Used by {@link KubeMQClient.receiveQueueMessages} and * {@link KubeMQClient.peekQueueMessages}. * * @see {@link KubeMQClient.receiveQueueMessages} * @see {@link ReceivedQueueMessage} */ interface QueuePollRequest { /** Queue channel to poll from. */ readonly channel: string; /** Maximum time in seconds to wait for messages before returning an empty result. */ readonly waitTimeoutSeconds: number; /** Maximum number of messages to receive in a single poll. Default: 1. */ readonly maxMessages?: number; /** * Automatically acknowledge messages upon receipt. Default: `false`. * @remarks **No effect on the unary receive API** — messages are always auto-acked server-side. * Use {@link KubeMQClient.streamQueueMessages} for explicit ack/reject control. */ readonly autoAck?: boolean; } /** * Result of a successful queue message send operation. * * @see {@link KubeMQClient.sendQueueMessage} * @see {@link QueueMessage} */ interface QueueSendResult { /** Server-assigned message ID. */ readonly messageId: string; /** Timestamp when the message was persisted by the server. */ readonly sentAt: Date; /** When the message will expire, if an expiration policy was set. */ readonly expirationAt?: Date; /** When the message becomes visible, if a delay policy was set. */ readonly delayedTo?: Date; } /** * Result of a batch queue message send operation. * * @remarks * Each item in `results` corresponds to the message at the same index in * the original batch. Check individual `error` fields for per-message failures. * * @see {@link KubeMQClient.sendQueueMessagesBatch} * @see {@link QueueSendResult} */ interface BatchSendResult { /** Per-message results, ordered by batch index. */ readonly results: { /** Index of the message in the original batch array. */ index: number; /** Server-assigned message ID on success. */ messageId?: string; /** Error details if this particular message failed. */ error?: KubeMQError; }[]; /** Number of messages successfully sent. */ readonly successCount: number; /** Number of messages that failed. */ readonly failureCount: number; } /** * Options for batch send operations. * * @see {@link KubeMQClient.sendQueueMessagesBatch} */ interface BatchSendOptions { /** Number of messages to include per batch request. */ readonly batchSize?: number; } /** * Options for creating a streaming queue consumer via {@link KubeMQClient.streamQueueMessages}. * * @remarks * The streaming API provides transactional message processing with explicit * ack/reject/requeue per message or per batch, unlike the simple poll API. * * @see {@link KubeMQClient.streamQueueMessages} * @see {@link QueueStreamHandle} */ interface QueueStreamOptions { /** Queue channel to consume from. */ readonly channel: string; /** Time in seconds to wait for messages before the stream returns empty. */ readonly waitTimeoutSeconds?: number; /** Maximum number of messages per batch. */ readonly maxMessages?: number; /** Automatically acknowledge messages after delivery to the handler. */ readonly autoAck?: boolean; /** Custom metadata key-value pairs sent with each downstream request. */ readonly metadata?: Record<string, string>; } /** * A queue message received via the streaming API with synchronous settlement methods. * * @remarks * Unlike {@link ReceivedQueueMessage} (from the poll API), the streaming message's * `ack()`, `nack()`, and `reQueue()` methods are synchronous — they write to * the underlying gRPC stream without awaiting a response. * * Each message within a transaction must be settled exactly once. * * @see {@link QueueStreamHandle} * @see {@link ReceivedQueueMessage} */ interface QueueStreamMessage { /** Server-assigned message ID. */ readonly id: string; /** Channel the message was received from. */ readonly channel: string; /** Raw message body. */ readonly body: Uint8Array; /** Application metadata string. */ readonly metadata: string; /** User-defined key-value tags. */ readonly tags: Record<string, string>; /** Server-side timestamp when the message was enqueued. */ readonly timestamp: Date; /** Monotonically increasing sequence number within the channel. */ readonly sequence: number; /** Number of times this message has been delivered. */ readonly receiveCount: number; /** MD5 hash of the body, if computed by the server. */ readonly md5OfBody?: string; /** Whether this message was re-routed from another queue. */ readonly isReRouted: boolean; /** Original queue channel if the message was re-routed. */ readonly reRouteFromQueue?: string; /** Expiration timestamp, if an expiration policy was set. */ readonly expiredAt?: Date; /** Delayed-until timestamp, if a delay policy was set. */ readonly delayedTo?: Date; /** Acknowledge the message, removing it from the queue. */ ack(): void; /** Reject (nack) the message, returning it to the queue for redelivery. */ nack(): void; /** Move the message to a different queue channel. */ reQueue(channel: string): void; } /** * Handle for an active streaming queue consumer, providing batch settlement and lifecycle control. * * @remarks * Obtained from {@link KubeMQClient.streamQueueMessages}. Register handlers * via `onMessages`, `onError`, and `onClose`, then settle messages individually * or in bulk. Call `close()` to gracefully shut down the stream. * * @see {@link KubeMQClient.streamQueueMessages} * @see {@link QueueStreamMessage} * @see {@link QueueStreamOptions} */ interface QueueStreamHandle { /** Whether the stream is still active and accepting operations. */ readonly isActive: boolean; /** Metadata returned by the most recent server response. */ readonly responseMetadata: Record<string, string>; /** Register a handler invoked when a batch of messages is received. */ onMessages(handler: (messages: QueueStreamMessage[]) => void): void; /** Register a handler invoked when a stream error occurs. */ onError(handler: (err: Error) => void): void; /** Register a handler invoked when the stream closes. */ onClose(handler: () => void): void; /*