UNPKG

@firtoz/socka

Version:

Standard Schema–first WebSocket RPC for TypeScript — Bun, Hono, Node ws, Cloudflare Workers, Durable Objects

176 lines (172 loc) • 8.13 kB
import { S as SockaContractBound, a as SockaWireFormat, p as SockaServerResponseFrame, n as SockaServerErrorFrame, o as SockaServerEventFrame, d as InferSockaSend, c as InferSockaPushPayload, b as InferSockaPushHandlers, m as SockaReportError } from '../socka-report-error-BALZPjd1.js'; export { B as reportSockaError } from '../socka-report-error-BALZPjd1.js'; import '@standard-schema/spec'; interface SockaWebSocketClientOptions<TContract extends SockaContractBound> { contract: TContract; /** Default `"json"` (text frames). Use `"msgpack"` for binary `ArrayBuffer` frames. */ wireFormat?: SockaWireFormat; url?: string; webSocket?: WebSocket; /** * When `false`, the socket is not created until {@link SockaWebSocketClient.connect} * (or the first operation that implicitly opens, e.g. {@link SockaSession} `send`). * Default `true`. */ autoConnect?: boolean; serializeJson?: (value: unknown) => string; deserializeJson?: (raw: string) => unknown; onOpen?: (event: Event) => void; onClose?: (event: CloseEvent) => void; onError?: (event: Event) => void; onResponse?: (frame: SockaServerResponseFrame) => void; onServerError?: (frame: SockaServerErrorFrame) => void; onEvent?: (frame: SockaServerEventFrame) => void; onValidationError?: (error: Error, rawMessage: unknown) => void; /** * Automatic reconnect after an abnormal close. **Disabled by default** when * {@link webSocket} is injected (tests); enabled when using {@link url}. * Pass `false` to disable. */ reconnect?: false | SockaReconnectConfig; /** Fired before a reconnect attempt is scheduled (after delay is chosen). */ onReconnecting?: (info: SockaReconnectingInfo) => void; /** Fired after a new socket reaches `open` following a reconnect. */ onReconnected?: (info: SockaReconnectedInfo) => void; } /** Options for {@link SockaWebSocketClientOptions.reconnect}. */ type SockaReconnectConfig = { /** Default `1000`. */ initialDelayMs?: number; /** Default `30000`. */ maxDelayMs?: number; /** 0–1 fraction of delay to randomize. Default `0.2`. */ jitter?: number; /** Omit for infinite attempts. */ maxAttempts?: number; /** * When `true` (default), delay reconnect until `document` is visible again. */ pauseWhenHidden?: boolean; }; type SockaReconnectingInfo = { attempt: number; delayMs: number; }; type SockaReconnectedInfo = { attempt: number; }; /** High-level connection lifecycle for UI and telemetry. */ type SockaConnectionStatus = "idle" | "connecting" | "open" | "reconnecting" | "closed"; /** * Browser WebSocket client driven by a socka contract. Sends client request * frames and dispatches decoded server frames to callbacks. */ declare class SockaWebSocketClient<TContract extends SockaContractBound> { private ws; private readonly opts; private readonly wireFormat; private readonly serializeJson; private readonly deserializeJson; private readonly onResponseCb?; private readonly onServerErrorCb?; private readonly onEventCb?; private readonly onValidationError?; readonly contract: TContract; private manualClose; private reconnectAttempt; private reconnectTimer; private _status; private readonly statusListeners; constructor(options: SockaWebSocketClientOptions<TContract>); private setStatus; /** Current connection lifecycle state. */ get status(): SockaConnectionStatus; /** * Subscribe to {@link status} changes. The listener is called immediately with the * current status, then on every transition. Returns an unsubscribe function. */ onStatusChange(listener: (status: SockaConnectionStatus) => void): () => void; private createSocket; private attachSocket; private getReconnectEnabled; private resolveReconnectConfig; private computeReconnectDelayMs; private maybeScheduleReconnect; private openReplacementSocket; private handleMessageEvent; /** * Creates the WebSocket (when {@link SockaWebSocketClientOptions.autoConnect} * was `false`) and waits until the connection is open. */ connect(): Promise<void>; sendRequest(id: string, rpc: string, body: Record<string, unknown>): void; close(code?: number, reason?: string): void; get readyState(): number; get socket(): WebSocket; waitForOpen(): Promise<void>; } type SockaSessionPushWaitOptions<TContract extends SockaContractBound, K extends keyof TContract["pushes"] & string> = { signal?: AbortSignal; timeoutMs?: number; predicate?: (payload: InferSockaPushPayload<TContract, K>) => boolean; }; type SockaSessionSubscribeApi<TContract extends SockaContractBound> = { on<K extends keyof TContract["pushes"] & string>(name: K, handler: (payload: InferSockaPushPayload<TContract, K>) => void | Promise<void>): void; off<K extends keyof TContract["pushes"] & string>(name: K, handler: (payload: InferSockaPushPayload<TContract, K>) => void | Promise<void>): void; once<K extends keyof TContract["pushes"] & string>(name: K, handler: (payload: InferSockaPushPayload<TContract, K>) => void | Promise<void>): void; waitForPush<K extends keyof TContract["pushes"] & string>(name: K, options?: SockaSessionPushWaitOptions<TContract, K>): Promise<InferSockaPushPayload<TContract, K>>; }; type SockaSessionOptions<TContract extends SockaContractBound> = Omit<SockaWebSocketClientOptions<TContract>, "onResponse" | "onServerError" | "onEvent"> & { onOpen?: (event: Event) => void; onClose?: (event: CloseEvent) => void; onError?: (event: Event) => void; pushHandlers?: Partial<InferSockaPushHandlers<TContract>>; /** * Optional sink for client-side push pipeline failures (listener throws, * push payload validation). Defaults to `console.error`; see * `SockaReportError` in `@firtoz/socka/core`. */ reportError?: (event: SockaReportError) => void; }; /** * Browser WebSocket session: **`session.send`** for contract calls, **`session.subscribe`** * for server pushes, **`session.client`** for low-level wire access. */ declare class SockaSessionBase<TContract extends SockaContractBound> { readonly client: SockaWebSocketClient<TContract>; readonly send: InferSockaSend<TContract>; readonly subscribe: SockaSessionSubscribeApi<TContract>; private readonly pending; private idSeq; private readonly pushListeners; private readonly reportError?; constructor(options: SockaSessionOptions<TContract>); private createSubscribeApi; private addPushListener; private removePushListener; private waitForPushImpl; private buildSendMethods; private call; private handleResponse; private handleServerError; private handleEvent; private dispatchValidatedPush; private nextId; rejectAllPending(reason: Error): void; close(code?: number, reason?: string): void; /** Opens the WebSocket when using {@link SockaWebSocketClientOptions.autoConnect} `false`. */ connect(): Promise<void>; /** Same as {@link SockaWebSocketClient.status}. */ get status(): SockaConnectionStatus; /** Same as {@link SockaWebSocketClient.onStatusChange}. */ onStatusChange(listener: (status: SockaConnectionStatus) => void): () => void; } interface SockaSessionConstructor { new <TContract extends SockaContractBound>(options: SockaSessionOptions<TContract>): SockaSession<TContract>; } type SockaSession<TContract extends SockaContractBound> = SockaSessionBase<TContract>; /** * WebSocket session: **`session.send`** for contract calls, **`session.subscribe`** for server pushes. */ declare const SockaSession: SockaSessionConstructor; export { type SockaConnectionStatus, type SockaReconnectConfig, type SockaReconnectedInfo, type SockaReconnectingInfo, SockaReportError, SockaSession, type SockaSessionConstructor, type SockaSessionOptions, type SockaSessionPushWaitOptions, type SockaSessionSubscribeApi, SockaWebSocketClient, type SockaWebSocketClientOptions };