trpc-uwebsockets
Version:
tRPC adapter for uWebSockets.js server
97 lines (96 loc) • 3.73 kB
TypeScript
import { type AnyRouter, type CreateContextCallback, inferRouterContext } from '@trpc/server';
import { NodeHTTPCreateContextFnOptions } from '@trpc/server/adapters/node-http';
import { type BaseHandlerOptions } from '@trpc/server/http';
import { type MaybePromise } from '@trpc/server/unstable-core-do-not-import';
import { URL } from 'url';
import type { TemplatedApp, WebSocket, WebSocketBehavior } from 'uWebSockets.js';
import { HttpResponseDecorated } from './fetchCompat';
type RemoveFunctions<T> = {
[K in keyof T as NonNullable<T[K]> extends Function ? never : K]: T[K];
};
/**
* A structure holding settings for a WebSocket handler.
*/
export type WebSocketBehaviorOptions = RemoveFunctions<WebSocketBehavior<any>>;
export type WebSocketConnection = WebSocket<WebsocketData>;
/**
* @public
*/
export type CreateWSSContextFnOptions = NodeHTTPCreateContextFnOptions<Request, HttpResponseDecorated> & {
client: WebSocketConnection;
};
export type CreateWSSContextFn<TRouter extends AnyRouter> = (opts: CreateWSSContextFnOptions) => MaybePromise<inferRouterContext<TRouter>>;
export type WSConnectionHandlerOptions<TRouter extends AnyRouter> = BaseHandlerOptions<TRouter, Request> & CreateContextCallback<inferRouterContext<TRouter>, CreateWSSContextFn<TRouter>>;
export type WebsocketsKeepAlive = {
/**
* Enable heartbeat messages
* @default false
*/
enabled: boolean;
/**
* Heartbeat interval in milliseconds
* @default 30_000
*/
pingMs?: number | undefined;
/**
* Terminate the WebSocket if no pong is received after this many milliseconds
* @default 5_000
*/
pongWaitMs?: number | undefined;
};
/**
* WebSockets handler definition
*/
export type WebsocketsHandlerOptions<TRouter extends AnyRouter> = WSConnectionHandlerOptions<TRouter> & {
/**
* Url path prefix where the tRPC server will be registered.
* @default ''
*/
prefix?: string | undefined;
/**
* Specify if SSL is used. Set to true if you are using SSLApp or if the server is served behind SSL reverse proxy.
* @default false
*/
ssl?: boolean | undefined;
keepAlive?: WebsocketsKeepAlive | undefined;
/**
* Disable responding to ping messages from the client
* **Not recommended** - this is mainly used for testing
* @default false
*/
dangerouslyDisablePong?: boolean | undefined;
/**
* uWebSockets.js WebSocket hander settings
*/
uWsBehaviorOptions?: WebSocketBehaviorOptions | undefined;
};
interface Completer {
promise: Promise<void>;
resolve: () => void;
reject: (error: Error) => void;
}
type WebsocketData = {
req: Request;
clientSubscriptions: Map<number | string, AbortController>;
abortController: AbortController;
ctx: inferRouterContext<AnyRouter> | undefined;
/**
* inside-out promise that resolves when context is ready.
*
* - when this is null, the context resolution will be started
* - otherwise all requests must await context resolution
*/
ctxCompleter: Completer | null;
keepAlive: KeepAliver | null;
url: URL;
};
export declare function getWSConnectionHandler<TRouter extends AnyRouter>(opts: WebsocketsHandlerOptions<TRouter>, allClients: Set<WebSocketConnection>): WebSocketBehavior<WebsocketData>;
export declare function applyWebsocketHandler<TRouter extends AnyRouter>(app: TemplatedApp, opts: WebsocketsHandlerOptions<TRouter>): {
broadcastReconnectNotification: () => void;
};
type KeepAliver = {
onMessage: () => void;
onClose: () => void;
};
export declare function handleKeepAlive(client: WebSocketConnection, pingMs?: number, pongWaitMs?: number): KeepAliver;
export {};