bun-ws-router
Version:
Lightweight client/server WebSocket router for Bun with type-safe Zod/Valibot validation.
52 lines (48 loc) • 2.19 kB
text/typescript
// SPDX-FileCopyrightText: 2025-present Kriasoft
// SPDX-License-Identifier: MIT
import { WebSocketRouter as BaseWebSocketRouter } from "../shared/router";
import { ValibotValidatorAdapter } from "./adapter";
import type {
MessageHandler as ValibotMessageHandler,
MessageSchemaType as ValibotMessageSchemaType,
WebSocketData,
} from "./types";
/**
* WebSocket router for Bun that provides type-safe message routing with Valibot validation.
* Routes incoming messages to handlers based on message type.
*
* ARCHITECTURE: This is a thin wrapper that binds the Valibot validator adapter to the
* shared router implementation. The adapter pattern allows swapping between Zod and
* Valibot without duplicating routing logic.
*
* @template T - Application-specific data to store with each WebSocket connection.
* Always includes a clientId property generated automatically.
* Example: { userId: string, roles: string[] }
*/
export class WebSocketRouter<
T extends Record<string, unknown> = Record<string, never>,
> extends BaseWebSocketRouter<T> {
constructor() {
// NOTE: ValibotValidatorAdapter handles schema validation and error formatting
// specific to Valibot. The base router handles all routing and connection logic.
super(new ValibotValidatorAdapter());
}
/**
* Registers a message handler with Valibot-specific type inference.
*
* This override provides validator-specific types for better IDE experience,
* but creates a Liskov Substitution Principle variance issue. The more specific
* handler signature means this class can't be used everywhere the base class is expected.
* This is an intentional trade-off for better developer experience.
*
* @see specs/adrs.md#ADR-001 - Type override solution for IDE inference
*/
// @ts-expect-error - Intentional override with more specific types for better DX
onMessage<Schema extends ValibotMessageSchemaType>(
schema: Schema,
handler: ValibotMessageHandler<Schema, WebSocketData<T>>,
): this {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return super.onMessage(schema as any, handler as any);
}
}