UNPKG

@ws-kit/valibot

Version:

Valibot validator adapter for WS-Kit with lightweight runtime validation and minimal bundle size

177 lines 6.52 kB
/** * Type-level schema branding and inference utilities for Valibot. * All operations are compile-time only (zero runtime cost). */ import type { InferOutput, GenericSchema } from "valibot"; import type { BrandedSchema as CoreBrandedSchema } from "@ws-kit/core"; /** * Schema branded with type metadata for inference. * Works entirely at the type level using TypeScript utility types. * @internal */ export type BrandedSchema<TType extends string = string, TPayload extends unknown = unknown, TResponse extends unknown = unknown, TMeta extends Record<string, unknown> = Record<string, never>> = CoreBrandedSchema<TType, TPayload, TResponse, TMeta>; /** * Helper type to infer actual types from GenericSchema. * Converts each Valibot schema in a shape to its inferred output type. * * @internal */ export type InferPayloadShape<P extends Record<string, GenericSchema> | GenericSchema | undefined> = P extends Record<string, GenericSchema> ? { [K in keyof P]: P[K] extends GenericSchema ? InferOutput<P[K]> : never; } : P extends GenericSchema ? InferOutput<P> : never; /** * Helper type to infer meta fields from a meta shape. * Includes standard fields (timestamp, correlationId) and extended fields. * * @internal */ export type InferMetaShape<M extends Record<string, GenericSchema> | undefined> = M extends Record<string, GenericSchema> ? { timestamp?: number; correlationId?: string; } & { [K in keyof M]: M[K] extends GenericSchema ? InferOutput<M[K]> : never; } : { timestamp?: number; correlationId?: string; }; /** * Event message schema: a Valibot schema with message type hint. * Returned by message() builder for fire-and-forget messages. * * Exactly captures what ctx.send() and router.on() expect at runtime: * a real Valibot schema with .safeParse() method and __descriptor metadata. */ export type MessageSchema = GenericSchema & { readonly __descriptor: { readonly messageType: string; }; }; /** * RPC request-response schema: a message schema with response definition. * Returned by rpc() builder for request-response patterns. */ export type RpcSchema = MessageSchema & { readonly response: GenericSchema; }; /** * Union of all schema types: event messages and RPC requests. * Use this for functions that accept any schema type. */ export type AnySchema = (MessageSchema & BrandedSchema) | (RpcSchema & BrandedSchema); /** * Extract message type literal from a branded schema. * * @example * ```typescript * const HelloOk = message("HELLO_OK", { text: v.string() }); * type Type = InferType<typeof HelloOk>; // "HELLO_OK" * ``` */ export type InferType<S> = S extends { readonly __descriptor: { readonly messageType: infer T; }; } ? T : S extends BrandedSchema<infer T, any, any, any> ? T : S extends CoreBrandedSchema<infer T, any, any, any> ? T : never; /** * Infer full inbound message type (as received by handlers). * * Includes optional timestamp/correlationId (may be present from client), * plus schema-defined extended meta and payload (if defined). * * @example * ```typescript * const HelloOk = message("HELLO_OK", { text: v.string() }); * type Msg = InferMessage<typeof HelloOk>; * // { type: "HELLO_OK", meta: { timestamp?: number, correlationId?: string }, payload: { text: string } } * * client.on(HelloOk, (msg) => { * msg.type // "HELLO_OK" (literal type) * msg.meta.timestamp // number | undefined * msg.payload.text // string * }); * ``` */ export type InferMessage<S> = S extends { readonly __descriptor: { readonly messageType: infer T; }; } ? S extends { readonly __valibot_payload: infer PayloadDef extends Record<string, GenericSchema> | GenericSchema | undefined; } ? { type: T; meta: { timestamp?: number; correlationId?: string; }; payload: InferPayloadShape<PayloadDef>; } : { type: T; meta: { timestamp?: number; correlationId?: string; }; } : never; /** * Infer payload type from schema, or never if no payload defined. * * Returns `never` (not `undefined`) for no-payload schemas to enable * clean overload discrimination in send() and request() methods. * * @example * ```typescript * const WithPayload = message("MSG", { id: v.number() }); * const NoPayload = message("PING"); * * type P1 = InferPayload<typeof WithPayload>; // { id: number } * type P2 = InferPayload<typeof NoPayload>; // never * ``` */ export type InferPayload<S> = S extends { readonly __valibot_payload: infer P extends Record<string, GenericSchema> | GenericSchema | undefined; } ? InferPayloadShape<P> : never; /** * Infer extended meta fields for outbound messages. * * Omits auto-injected fields (timestamp, correlationId) which are provided * via opts.meta or opts.correlationId. Only includes schema-defined extended meta. * * Used to enforce required extended meta fields at compile time for send/request. * * @example * ```typescript * const RoomMsg = message("CHAT", { text: v.string() }, { roomId: v.string() }); * type Meta = InferMeta<typeof RoomMsg>; // { roomId: string } * // timestamp and correlationId are omitted (auto-injected by client) * * client.send(RoomMsg, { text: "hi" }, { meta: { roomId: "general" } }); * ``` */ export type InferMeta<S> = S extends { readonly __descriptor: { readonly messageType: infer T extends string; }; } ? S extends BrandedSchema<T, any, any, infer M> ? M : S extends CoreBrandedSchema<T, any, any, infer M> ? M : never : never; /** * Extract response type from an RPC schema. * Returns never if no response is defined. * * @example * ```typescript * const GetUser = rpc("GET_USER", { id: v.string() }, "USER", { id: v.string(), name: v.string() }); * type Response = InferResponse<typeof GetUser>; // { id: string, name: string } * ``` */ export type InferResponse<S> = S extends { readonly response: infer Res; } ? Res extends { readonly __valibot_payload: infer P extends Record<string, GenericSchema> | GenericSchema | undefined; } ? InferPayloadShape<P> : never : never; /** Validator-agnostic safeParse result (Zod/Valibot compatible). */ export interface SafeParseResult<T = unknown> { readonly success: boolean; readonly data: T | undefined; readonly issues: readonly unknown[] | undefined; } /** Re-export shared types that are validator-agnostic. See: @ws-kit/core */ export type { WebSocketData } from "@ws-kit/core"; //# sourceMappingURL=types.d.ts.map