// SPDX-FileCopyrightText: 2025-present Kriasoft // SPDX-License-Identifier: MIT /** * Typed WebSocket client adapter for Zod schemas. * * Provides full type inference for message handlers via type override pattern (ADR-002). * Zero runtime overhead - pure type-level wrapper around generic client. * * @see specs/adrs.md#ADR-002 - Type override rationale * @see specs/client.md - Client API specification */ import { createClient as createGenericClient } from "../client/index.js"; import type { AnyInboundMessage, ClientOptions, ClientState, WebSocketClient, } from "../client/types.js"; import type { InferMessage, InferMeta, InferPayload, MessageSchemaType as ZodMessageSchema, } from "./types.js"; // Re-export base types and error classes export * from "../client/types.js"; export type { InferMessage, InferMeta, InferPayload, MessageSchemaType as ZodMessageSchema, } from "./types.js"; /** * Options for send() method with typed meta field inference. */ interface SendOptions { meta?: InferMeta; correlationId?: string; } /** * Options for request() method with typed meta field inference. */ interface RequestOptions extends SendOptions { timeoutMs?: number; signal?: AbortSignal; } /** * Typed WebSocket client interface with Zod schema inference. * * Overrides generic client methods to provide full type safety: * - on(): Handler receives InferMessage (typed msg with payload/meta) * - send(): Payload conditional typing via overloads * - request(): Returns Promise> * * @see specs/adrs.md#ADR-002 - Type override implementation pattern */ export interface ZodWebSocketClient extends Omit { readonly state: ClientState; readonly isConnected: boolean; readonly protocol: string; connect(): Promise; close(opts?: { code?: number; reason?: string }): Promise; onState(cb: (state: ClientState) => void): () => void; onceOpen(): Promise; /** * Register typed message handler. * Handler receives fully typed message inferred from schema. */ on( schema: S, handler: (msg: InferMessage) => void, ): () => void; /** * Send message with payload (schema defines payload field). * Payload type inferred from schema, required at compile time. */ send( schema: S, payload: InferPayload, opts?: SendOptions, ): InferPayload extends never ? never : boolean; /** * Send message without payload (schema has no payload field). * Payload parameter omitted at compile time. */ send( schema: S, opts?: SendOptions, ): InferPayload extends never ? boolean : never; /** * Request/response with typed reply (with payload). * Payload type inferred from schema, required at compile time. */ request( schema: S, payload: InferPayload, reply: R, opts?: RequestOptions, ): InferPayload extends never ? never : Promise>; /** * Request/response with typed reply (no payload). * Payload parameter omitted at compile time. */ request( schema: S, reply: R, opts?: RequestOptions, ): InferPayload extends never ? Promise> : never; /** * Hook for unhandled message types. * Receives structurally valid messages with no registered schema. */ onUnhandled(cb: (msg: AnyInboundMessage) => void): () => void; /** * Hook for non-fatal internal errors. * Fires for: parse failures, validation failures, queue overflow. */ onError( cb: ( error: Error, context: { type: "parse" | "validation" | "overflow" | "unknown"; details?: unknown; }, ) => void, ): () => void; } /** * Create typed WebSocket client with Zod schema inference. * * Pure type cast - zero runtime overhead compared to generic client. * All type safety is compile-time only via TypeScript inference. * * @example * ```typescript * import { z } from "zod"; * import { createMessageSchema } from "bun-ws-router/zod"; * import { createClient } from "bun-ws-router/zod/client"; * * const { messageSchema } = createMessageSchema(z); * const HelloOk = messageSchema("HELLO_OK", { text: z.string() }); * * const client = createClient({ url: "wss://api.example.com" }); * * client.on(HelloOk, (msg) => { * // ✅ msg fully typed: { type: "HELLO_OK", meta: {...}, payload: { text: string } } * console.log(msg.payload.text.toUpperCase()); * }); * ``` * * @see specs/client.md - Full client API documentation * @see specs/adrs.md#ADR-002 - Type override implementation details */ export function createClient(opts: ClientOptions): ZodWebSocketClient { return createGenericClient(opts) as ZodWebSocketClient; }