@qubit-rs/client
Version:
Seamless RPC for Rust & TypeScript
151 lines (139 loc) • 5.17 kB
TypeScript
type RpcRequest = {
jsonrpc: "2.0";
method: string;
id: string | number;
params: unknown;
};
/**
* A JSONRPC-2.0 error.
*/
type RpcError = {
code: number;
message: string;
data: any;
};
/**
* An incoming message for a specific subscription
*/
type RpcSubscriptionMessage<T> = {
type: "message";
id: string | number;
value: T;
};
type RpcResponse<T> = {
type: "ok";
id: string | number;
value: T;
} | {
type: "error";
id: string | number;
value: RpcError;
} | RpcSubscriptionMessage<T>;
type SocketOptions = {
WebSocket: unknown;
};
declare function ws(host: string, socket_options?: SocketOptions): {
query: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<any>>;
mutate: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<any>>;
subscribe: (id: string | number, on_data: ((value: any) => void) | undefined) => () => void;
};
type HttpOptions = {
fetch?: typeof fetch;
};
declare function http(host: string, http_options?: HttpOptions): {
query: (_id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
mutate: (_id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
};
type MultiOptions = {
ws?: SocketOptions;
http?: HttpOptions;
};
/**
* Transport that combines both `http` for query and mutate, and `ws` for subscriptions.
*/
declare function multi(host: string, options?: MultiOptions): {
query: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
mutate: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
subscribe: (id: string | number, on_data: ((value: any) => void) | undefined) => () => void;
};
type ClientBuilder<Server> = (host: string) => Server;
/**
* Interface required for a transport.
*/
type Transport = {
/**
* Make a request that is a query, meaning that it is safe to be cached.
*/
query: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
/**
* Make a request that is a mutation, meaning that it should not be cached.
*/
mutate: (id: string | number, payload: RpcRequest) => Promise<RpcResponse<unknown> | null>;
/**
* Start a subscription, calling `on_data` for every message from the server. An unsubscribe
* method must be returned, which must terminate the subscription when called.
*/
subscribe?: (id: string | number, on_data?: (value: any) => void) => () => void;
};
type Query<Args extends any[], Return> = {
query: (...args: Args) => Promise<Return>;
};
type Mutation<Args extends any[], Return> = {
mutate: (...args: Args) => Promise<Return>;
};
type StreamHandlers<T> = {
on_data: (data: T) => void;
on_error: (error: Error) => void;
on_end: () => void;
};
type StreamHandler<T> = ((data: T) => void) | Partial<StreamHandlers<T>>;
type StreamUnsubscribe = () => void;
/**
* Helper type to add handler to a list of arguments, in a way that it will be named.
*/
type AddHandler<Arr extends any[], Item> = [...Arr, handler: StreamHandler<Item>];
type Subscription<Args extends any[], Item> = {
subscribe: (...args: AddHandler<Args, Item>) => StreamUnsubscribe;
};
/**
* A handler function which must always take the path, represented as an array of strings.
*/
type HandlerFn<TArgs extends any[], TReturn> = (path: string[], ...args: TArgs) => TReturn;
/**
* Strips the `path` parameter (first parameter) from a `HandlerFn`. This represents the function
* that is exposed to the end-user.
*/
type StripPath<F> = F extends HandlerFn<infer TArgs, infer TReturn> ? (...args: TArgs) => TReturn : never;
/**
* A collection of plugins, meaning handlers that include the path parameter.
*/
type Plugins = Record<string, HandlerFn<any[], any>>;
/**
* For all available handlers, will produce a handler that has the `path` parameter stripped from
* it.
*/
type Handlers<THandlers extends Plugins> = {
[K in keyof THandlers]: StripPath<THandlers[K]>;
};
/**
* Determines if the provided type has a nested object, or is just made up of functions.
*/
type HasNestedObject<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never;
};
type AtEdge<T, Yes, No> = T extends HasNestedObject<T> ? Yes : No;
/**
* Inject the provided plugins into the edges of the server.
*/
type InjectPlugins<TServer, TPlugins extends Plugins> = AtEdge<TServer, TServer & TPlugins, {
[K in keyof TServer]: InjectPlugins<TServer[K], TPlugins>;
}>;
/**
* Build a new client for a server.
*/
declare function build_client<Server>(client: Transport): Server;
/**
* Build a new client and inject the following plugins.
*/
declare function build_client<Server, TPlugins extends Plugins>(transport: Transport, plugins: TPlugins): InjectPlugins<Server, Handlers<TPlugins>>;
export { type ClientBuilder, type HandlerFn, type HttpOptions, type MultiOptions, type Mutation, type Plugins, type Query, type SocketOptions, type StreamHandler, type StreamUnsubscribe, type Subscription, type Transport, build_client, http, multi, ws };