UNPKG

tsrpc

Version:

A TypeScript RPC Framework, with runtime type checking and built-in serialization, support both HTTP and WebSocket.

923 lines (875 loc) 36.2 kB
/*! * TSRPC v3.4.19 * ----------------------------------------- * Copyright (c) King Wang. * MIT License * https://github.com/k8w/tsrpc */ /// <reference types="node" /> /// <reference path="mongodb-polyfill.d.ts" /> import { ApiReturn } from 'tsrpc-proto'; import { ApiService } from 'tsrpc-base-client'; import { ApiServiceDef } from 'tsrpc-proto'; import { BaseHttpClient } from 'tsrpc-base-client'; import { BaseHttpClientOptions } from 'tsrpc-base-client'; import { BaseServiceType } from 'tsrpc-proto'; import { BaseWsClient } from 'tsrpc-base-client'; import { BaseWsClientOptions } from 'tsrpc-base-client'; import { Counter } from 'tsrpc-base-client'; import { CustomTypeSchema } from 'tsbuffer-schema'; import { Flow } from 'tsrpc-base-client'; import http from 'http'; import * as http_2 from 'http'; import https from 'https'; import { Logger } from 'tsrpc-proto'; import { LogLevel } from 'tsrpc-proto'; import { MsgService } from 'tsrpc-base-client'; import { ParsedServerInput } from 'tsrpc-base-client'; import { ServiceMap } from 'tsrpc-base-client'; import { ServiceProto } from 'tsrpc-proto'; import { TSBuffer } from 'tsbuffer'; import { TsrpcError } from 'tsrpc-proto'; import { TsrpcErrorData } from 'tsrpc-proto'; import * as WebSocket_2 from 'ws'; /** * A call request by `client.callApi()` * @typeParam Req - Type of request * @typeParam Res - Type of response * @typeParam ServiceType - The same `ServiceType` to server, it is used for code auto hint. */ export declare abstract class ApiCall<Req = any, Res = any, ServiceType extends BaseServiceType = any> extends BaseCall<ServiceType> { readonly type: "api"; /** * Which `ApiService` the request is calling for */ readonly service: ApiService; /** Only exists in long connection, it is used to associate request and response. * It is created by the client, and the server would return the same value in `ApiReturn`. */ readonly sn?: number; /** * Request data from the client, type of it is checked by the framework already. */ readonly req: Req; constructor(options: ApiCallOptions<Req, ServiceType>, logger?: PrefixLogger); protected _return?: ApiReturn<Res>; /** * Response Data that sent already. * `undefined` means no return data is sent yet. (Never `call.succ()` and `call.error()`) */ get return(): ApiReturn<Res> | undefined; protected _usedTime: number | undefined; /** Time from received req to send return data */ get usedTime(): number | undefined; /** * Send a successful `ApiReturn` with response data * @param res - Response data * @returns Promise resolved means the buffer is sent to kernel */ succ(res: Res): Promise<void>; /** * Send a error `ApiReturn` with a `TsrpcError` * @returns Promise resolved means the buffer is sent to kernel */ error(message: string, info?: Partial<TsrpcErrorData>): Promise<void>; error(err: TsrpcError): Promise<void>; protected _prepareReturn(ret: ApiReturn<Res>): Promise<void>; protected _sendReturn(ret: ApiReturn<Res>): ReturnType<SendReturnMethod<Res>>; static encodeApiReturn(tsbuffer: TSBuffer, service: ApiService, apiReturn: ApiReturn<any>, type: "text", sn?: number): EncodeApiReturnOutput<string>; static encodeApiReturn(tsbuffer: TSBuffer, service: ApiService, apiReturn: ApiReturn<any>, type: "buffer", sn?: number): EncodeApiReturnOutput<Uint8Array>; static encodeApiReturn(tsbuffer: TSBuffer, service: ApiService, apiReturn: ApiReturn<any>, type: "json", sn?: number): EncodeApiReturnOutput<object>; static encodeApiReturn(tsbuffer: TSBuffer, service: ApiService, apiReturn: ApiReturn<any>, type: "text" | "buffer" | "json", sn?: number): EncodeApiReturnOutput<Uint8Array> | EncodeApiReturnOutput<string> | EncodeApiReturnOutput<object>; } export declare class ApiCallHttp<Req = any, Res = any, ServiceType extends BaseServiceType = any> extends ApiCall<Req, Res, ServiceType> { readonly conn: HttpConnection<ServiceType>; constructor(options: ApiCallHttpOptions<Req, ServiceType>); protected _sendReturn(ret: ApiReturn<Res>): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; }>; } export declare interface ApiCallHttpOptions<Req, ServiceType extends BaseServiceType> extends ApiCallOptions<Req, ServiceType> { conn: HttpConnection<ServiceType>; } export declare interface ApiCallOptions<Req, ServiceType extends BaseServiceType> extends BaseCallOptions<ServiceType> { /** Which service the Call is belong to */ service: ApiService; /** Only exists in long connection, it is used to associate request and response. * It is created by the client, and the server would return the same value in `ApiReturn`. */ sn?: number; /** Request Data */ req: Req; } export declare class ApiCallWs<Req = any, Res = any, ServiceType extends BaseServiceType = any> extends ApiCall<Req, Res, ServiceType> { readonly conn: WsConnection<ServiceType>; constructor(options: ApiCallWsOptions<Req, ServiceType>); protected _prepareReturn(ret: ApiReturn<Res>): Promise<void>; } export declare interface ApiCallWsOptions<Req, ServiceType extends BaseServiceType> extends ApiCallOptions<Req, ServiceType> { conn: WsConnection<ServiceType>; } export declare type ApiHandler<Call extends ApiCall = ApiCall> = (call: Call) => void | Promise<void>; export declare abstract class BaseCall<ServiceType extends BaseServiceType> { readonly conn: BaseConnection<ServiceType>; readonly service: ApiService | MsgService; /** Time that server created the call */ readonly startTime: number; readonly logger: PrefixLogger; constructor(options: BaseCallOptions<ServiceType>, logger: PrefixLogger); get server(): this["conn"]["server"]; } export declare interface BaseCallOptions<ServiceType extends BaseServiceType> { /** Connection */ conn: BaseConnection<ServiceType>; /** Which service the call is belong to */ service: ApiService | MsgService; } export declare abstract class BaseConnection<ServiceType extends BaseServiceType = any> { /** It is long connection or short connection */ abstract readonly type: "LONG" | "SHORT"; protected abstract readonly ApiCallClass: { new (options: any): ApiCall; }; protected abstract readonly MsgCallClass: { new (options: any): MsgCall; }; /** Connection unique ID */ readonly id: string; /** Client IP address */ readonly ip: string; readonly server: BaseServer<ServiceType>; readonly logger: PrefixLogger; dataType: BaseConnectionOptions["dataType"]; constructor(options: BaseConnectionOptions<ServiceType>, logger: PrefixLogger); abstract get status(): ConnectionStatus; /** Close the connection */ abstract close(reason?: string): void; /** Send buffer (with pre-flow and post-flow) */ sendData(data: string | Uint8Array | object, call?: ApiCall): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; canceledByFlow?: string; }>; protected abstract doSendData(data: string | Uint8Array | object, call?: ApiCall): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; }>; makeCall(input: ParsedServerInput): ApiCall | MsgCall; /** * Send message to the client, only be available when it is long connection. * @param msgName * @param msg - Message body * @returns Promise resolved when the buffer is sent to kernel, it not represents the server received it. */ sendMsg<T extends string & keyof ServiceType["msg"]>(msgName: T, msg: ServiceType["msg"][T]): ReturnType<BaseConnection["sendData"]>; private _msgHandlers?; /** * Add a message handler, * duplicate handlers to the same `msgName` would be ignored. * @param msgName * @param handler */ listenMsg<Msg extends string & keyof ServiceType["msg"], Call extends MsgCall<ServiceType["msg"][Msg]>>(msgName: Msg, handler: MsgHandler<Call>): MsgHandler<Call>; /** * Remove a message handler */ unlistenMsg<Msg extends string & keyof ServiceType["msg"], Call extends MsgCall<ServiceType["msg"][Msg]>>(msgName: Msg, handler: Function): void; /** * Remove all handlers from a message */ unlistenMsgAll<Msg extends string & keyof ServiceType["msg"], Call extends MsgCall<ServiceType["msg"][Msg]>>(msgName: Msg): void; } export declare interface BaseConnectionOptions<ServiceType extends BaseServiceType = any> { /** Created by server, each Call has a unique id. */ id: string; /** Client IP address */ ip: string; server: BaseServer<ServiceType>; dataType: "text" | "buffer" | "json"; } /** * Abstract base class for TSRPC Server. * Implement on a transportation protocol (like HTTP WebSocket) by extend it. * @typeParam ServiceType - `ServiceType` from generated `proto.ts` */ export declare abstract class BaseServer<ServiceType extends BaseServiceType = BaseServiceType> { /** * Start the server * @throws */ abstract start(): Promise<void>; /** * Stop server immediately, not waiting for the requests ending. */ abstract stop(): Promise<void>; protected _status: ServerStatus; get status(): ServerStatus; readonly proto: ServiceProto<ServiceType>; readonly options: BaseServerOptions<ServiceType>; readonly tsbuffer: TSBuffer; readonly serviceMap: ServiceMap; readonly logger: Logger; protected _connIdCounter: Counter; /** * Flow is a specific concept created by TSRPC family. * All pre-flow can interrupt latter behaviours. * All post-flow can NOT interrupt latter behaviours. */ readonly flows: { /** After the connection is created */ readonly postConnectFlow: Flow<BaseConnection<ServiceType>>; /** After the connection is disconnected */ readonly postDisconnectFlow: Flow<{ conn: BaseConnection<ServiceType>; reason?: string | undefined; }>; /** * Before processing the received data, usually be used to encryption / decryption. * Return `null | undefined` would ignore the buffer. */ readonly preRecvDataFlow: Flow<{ conn: BaseConnection<ServiceType>; data: string | Uint8Array | object; /** * @deprecated use `serviceId` instead */ serviceName?: string | undefined; /** * Parsed service id, you can get this by `this.serviceMap.apiName2Service[serviceName].id` */ serviceId?: number | undefined; }>; /** * Before send out data to network, usually be used to encryption / decryption. * Return `null | undefined` would not send the buffer. */ readonly preSendDataFlow: Flow<{ conn: BaseConnection<ServiceType>; data: string | Uint8Array | object; call?: ApiCall<any, any, any> | undefined; }>; /** * @deprecated Use `preRecvDataFlow` instead. */ readonly preRecvBufferFlow: Flow<{ conn: BaseConnection<ServiceType>; buf: Uint8Array; }>; /** * @deprecated Use `preSendDataFlow` instead. */ readonly preSendBufferFlow: Flow<{ conn: BaseConnection<ServiceType>; buf: Uint8Array; call?: ApiCall<any, any, any> | undefined; }>; /** * Before a API request is send. * Return `null | undefined` would cancel the request. */ readonly preApiCallFlow: Flow<ApiCall<any, any, any>>; /** * Before return the `ApiReturn` to the client. * It may be used to change the return value, or return `null | undefined` to abort the request. */ readonly preApiReturnFlow: Flow<{ call: ApiCall; return: ApiReturn<any>; }>; /** * After the `ApiReturn` is send. * return `null | undefined` would NOT interrupt latter behaviours. */ readonly postApiReturnFlow: Flow<{ call: ApiCall; return: ApiReturn<any>; }>; /** * After the api handler is executed. * return `null | undefined` would NOT interrupt latter behaviours. */ readonly postApiCallFlow: Flow<ApiCall<any, any, any>>; /** * Before handle a `MsgCall` */ readonly preMsgCallFlow: Flow<MsgCall<any, any>>; /** * After handlers of a `MsgCall` are executed. * return `null | undefined` would NOT interrupt latter behaviours. */ readonly postMsgCallFlow: Flow<MsgCall<any, any>>; /** * Before send out a message. * return `null | undefined` would NOT interrupt latter behaviours. */ readonly preSendMsgFlow: Flow<{ conn: BaseConnection<ServiceType>; service: MsgService; msg: any; }>; /** * After send out a message. * return `null | undefined` would NOT interrupt latter behaviours. */ readonly postSendMsgFlow: Flow<{ conn: BaseConnection<ServiceType>; service: MsgService; msg: any; }>; }; private _apiHandlers; private _msgHandlers; private static _isUncaughtExceptionProcessed; /** * It makes the `uncaughtException` and `unhandledRejection` not lead to the server stopping. * @param logger * @returns */ static processUncaughtException(logger: Logger): void; constructor(proto: ServiceProto<ServiceType>, options: BaseServerOptions<ServiceType>); protected _setDefaultFlowOnError(): void; protected _pendingApiCallNum: number; /** * Process the buffer, after the `preRecvBufferFlow`. */ _onRecvData(conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, serviceId?: number): Promise<void>; protected _handleApiCall(call: ApiCall): Promise<void>; protected _onApiCall(call: ApiCall): Promise<void>; protected _onMsgCall(call: MsgCall): Promise<void>; /** * Associate a `ApiHandler` to a specific `apiName`. * So that when `ApiCall` is receiving, it can be handled correctly. * @param apiName * @param handler */ implementApi<Api extends string & keyof ServiceType["api"], Call extends ApiCall<ServiceType["api"][Api]["req"], ServiceType["api"][Api]["res"]>>(apiName: Api, handler: ApiHandler<Call>): void; /** 用于延迟注册 API */ protected _delayImplementApiPath?: string; /** * Auto call `imeplementApi` by traverse the `apiPath` and find all matched `PtlXXX` and `ApiXXX`. * It is matched by checking whether the relative path and name of an API is consistent to the service name in `serviceProto`. * Notice that the name prefix of protocol is `Ptl`, of API is `Api`. * For example, `protocols/a/b/c/PtlTest` is matched to `api/a/b/c/ApiTest`. * @param apiPath Absolute path or relative path to `process.cwd()`. * @returns */ autoImplementApi(apiPath: string, delay?: boolean): Promise<{ succ: string[]; fail: string[]; }>; getApiHandler(svc: ApiServiceDef, apiPath?: string, logger?: Logger): Promise<{ handler: ApiHandler; errMsg?: undefined; } | { handler?: undefined; errMsg: string; }>; /** * Add a message handler, * duplicate handlers to the same `msgName` would be ignored. * @param msgName * @param handler * @returns */ listenMsg<T extends keyof ServiceType["msg"], Call extends MsgCall<ServiceType["msg"][T]>>(msgName: T | RegExp, handler: MsgHandler<Call>): MsgHandler<Call>; /** * Remove a message handler */ unlistenMsg<T extends keyof ServiceType["msg"]>(msgName: T | RegExp, handler: Function): void; /** * Remove all handlers from a message */ unlistenMsgAll<T extends keyof ServiceType["msg"]>(msgName: T | RegExp): void; /** * Event when the server cannot parse input buffer to api/msg call. * By default, it will return "Input Data Error" . */ onInputDataError(errMsg: string, conn: BaseConnection<ServiceType>, data: string | Uint8Array | object): Promise<void>; /** * Event when a uncaught error (except `TsrpcError`) is throwed. * By default, it will return a `TsrpcError` with message "Internal server error". * If `returnInnerError` is `true`, the original error would be returned as `innerErr` property. */ onInternalServerError(err: { message: string; stack?: string; name?: string; }, call: ApiCall): void; protected _gracefulStop?: { rs: () => void; }; /** * Stop the server gracefully. * Wait all API requests finished and then stop the server. * @param maxWaitTime - The max time(ms) to wait before force stop the server. * `undefined` and `0` means unlimited time. */ gracefulStop(maxWaitTime?: number): Promise<void>; /** * Execute API function through the inner connection, which is useful for unit test. * * **NOTICE** * The `req` and return value is native JavaScript object which is not compatible to JSON. (etc. ArrayBuffer, Date, ObjectId) * If you are using pure JSON as transfering, you may need use `callApiByJSON`. * @param apiName * @param req * @param options */ callApi<T extends string & keyof ServiceType["api"]>(apiName: T, req: ServiceType["api"][T]["req"]): Promise<ApiReturn<ServiceType["api"][T]["res"]>>; /** * Like `server.callApi`, but both input and output are pure JSON object, * which can be `JSON.stringify()` and `JSON.parse()` directly. * Types that not compatible to JSON, would be encoded and decoded automatically. * @param apiName - The same with `server.callApi`, may be parsed from the URL. * @param jsonReq - Request data in pure JSON * @returns Encoded `ApiReturn<Res>` in pure JSON */ /** * Process JSON request by inner proxy, this is useful when you are porting to cloud function services. * Both the input and output is pure JSON, ArrayBuffer/Date/ObjectId are encoded to string automatically. * @param apiName - Parsed from URL * @param req - Pure JSON * @param logger - Custom logger * @returns - Pure JSON */ inputJSON(apiName: string, req: object, logger?: PrefixLogger): Promise<ApiReturn<object>>; /** * Process input buffer by inner proxy, this is useful when you are porting to cloud function services. * @param buf Input buffer (may be sent by TSRPC client) * @returns Response buffer */ inputBuffer(buf: Uint8Array): Promise<Uint8Array>; protected _parseServerInput(tsbuffer: TSBuffer, serviceMap: ServiceMap, data: string | Uint8Array | object, serviceId?: number): { isSucc: true; result: ParsedServerInput; } | { isSucc: false; errMsg: string; }; } export declare interface BaseServerOptions<ServiceType extends BaseServiceType> { /** * Whether to enable JSON compatible mode. * When it is true, it can be compatible with typical HTTP JSON request (like RESTful API). * * @remarks * The JSON request methods are: * * 1. Add `Content-type: application/json` to request header. * 2. HTTP request is: `POST /{jsonUrlPath}/{apiName}`. * 3. POST body is JSON string. * 4. The response body is JSON string also. * * NOTICE: Buffer type are not supported due to JSON not support them. * For security and efficient reason, we strongly recommend you use binary encoded transportation. * * @defaultValue `false` */ json: boolean; /** @deprecated Use `json` instead. */ jsonEnabled?: boolean; /** * Whether to strictly distinguish between `null` and `undefined` when encoding, decoding, and type checking. * @defaultValue false */ strictNullChecks: boolean; /** * Timeout for processing an `ApiCall`(ms) * `0` and `undefined` means unlimited time * @defaultValue 30000 */ apiTimeout: number | undefined; /** * Logger for processing log * @defaultValue `new TerminalColorLogger()` (print to console with color) */ logger: Logger; /** * The minimum log level of `logger` * @defaultValue `debug` */ logLevel: LogLevel; /** * Whether to print API request body into log (may increase log size) * @defaultValue `true` */ logReqBody: boolean; /** * Whether to print API response body into log (may increase log size) * @defaultValue `true` */ logResBody: boolean; /** * Whether to print `[SendMsg]` and `[RecvMsg]` log into log * @defaultValue `true` */ logMsg: boolean; /** * If `true`, all sent and received raw buffer would be print into the log. * It may be useful when you do something for buffer encryption/decryption, and want to debug them. */ debugBuf?: boolean; /** * When uncaught error throwed, * whether to return the original error as a property `innerErr`. * (May include some sensitive information, suggests set to `false` in production environment.) * @defaultValue It depends on environment variable `NODE_ENV`. * If `NODE_ENV` equals to `production`, the default value is `false`, otherwise is `true`. */ returnInnerError: boolean; /** * Customize the protocol data type */ customTypes?: { [schemaId: string]: CustomTypeSchema; }; } export declare enum ConnectionStatus { Opened = "OPENED", Closing = "CLOSING", Closed = "CLOSED" } export declare const defaultBaseServerOptions: BaseServerOptions<any>; export declare const defaultHttpServerOptions: HttpServerOptions<any>; export declare type EncodeApiReturnOutput<T> = { isSucc: true; /** Encoded binary buffer */ output: T; errMsg?: undefined; } | { isSucc: false; /** Error message */ errMsg: string; output?: undefined; }; /** * Client for TSRPC HTTP Server. * It uses native http module of NodeJS. * @typeParam ServiceType - `ServiceType` from generated `proto.ts` */ export declare class HttpClient<ServiceType extends BaseServiceType> extends BaseHttpClient<ServiceType> { readonly options: Readonly<HttpClientOptions>; constructor(proto: ServiceProto<ServiceType>, options?: Partial<HttpClientOptions>); } export declare interface HttpClientOptions extends BaseHttpClientOptions { /** NodeJS HTTP Agent */ agent?: http.Agent | https.Agent; } export declare class HttpConnection<ServiceType extends BaseServiceType = any> extends BaseConnection<ServiceType> { readonly type = "SHORT"; protected readonly ApiCallClass: typeof ApiCallHttp; protected readonly MsgCallClass: typeof MsgCallHttp; readonly httpReq: http_2.IncomingMessage & { rawBody?: Buffer; }; readonly httpRes: http_2.ServerResponse; readonly server: HttpServer<ServiceType>; /** * Whether the transportation of the connection is JSON encoded instead of binary encoded. */ readonly isJSON: boolean | undefined; /** * In short connection, one connection correspond one call. * It may be `undefined` when the request data is not fully received yet. */ call?: ApiCallHttp | MsgCallHttp; constructor(options: HttpConnectionOptions<ServiceType>); get status(): ConnectionStatus; protected doSendData(data: string | Uint8Array, call?: ApiCall): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; }>; /** * Close the connection, the reason would be attached to response header `X-TSRPC-Close-Reason`. */ close(reason?: string): void; makeCall(input: ParsedServerInput): ApiCallHttp | MsgCallHttp; } export declare interface HttpConnectionOptions<ServiceType extends BaseServiceType> extends BaseConnectionOptions<ServiceType> { server: HttpServer<ServiceType>; httpReq: http_2.IncomingMessage; httpRes: http_2.ServerResponse; } /** * TSRPC Server, based on HTTP connection. * @typeParam ServiceType - `ServiceType` from generated `proto.ts` */ export declare class HttpServer<ServiceType extends BaseServiceType = any> extends BaseServer<ServiceType> { readonly options: HttpServerOptions<ServiceType>; constructor(proto: ServiceProto<ServiceType>, options?: Partial<HttpServerOptions<ServiceType>>); /** Native `http.Server` of NodeJS */ httpServer?: http_2.Server | https.Server; /** * {@inheritDoc BaseServer.start} */ start(): Promise<void>; /** * {@inheritDoc BaseServer.stop} */ stop(): Promise<void>; } export declare interface HttpServerOptions<ServiceType extends BaseServiceType> extends BaseServerOptions<ServiceType> { /** Which port the HTTP server listen to */ port: number; /** * HTTPS options, the server would use https instead of http if this value is defined. * NOTICE: Once you enabled https, you CANNOT visit the server via `http://` anymore. * If you need visit the server via both `http://` and `https://`, you can start 2 HttpServer (one with `https` and another without). * @defaultValue `undefined` */ https?: { /** * @example * fs.readFileSync('xxx-key.pem'); */ key: https.ServerOptions["key"]; /** * @example * fs.readFileSync('xxx-cert.pem'); */ cert: https.ServerOptions["cert"]; }; /** * Passed to the `timeout` property to the native `http.Server` of NodeJS, in milliseconds. * `0` and `undefined` will disable the socket timeout behavior. * NOTICE: this `socketTimeout` be `undefined` only means disabling of the socket timeout, the `apiTimeout` is still working. * `socketTimeout` should always greater than `apiTimeout`. * @defaultValue `undefined` * @see {@link https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_server_timeout} */ socketTimeout?: number; /** * Passed to the `keepAliveTimeout` property to the native `http.Server` of NodeJS, in milliseconds. * It means keep-alive timeout of HTTP socket connection. * @defaultValue 5000 (from NodeJS) * @see {@link https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_server_keepalivetimeout} */ keepAliveTimeout?: number; /** * Response header value of `Access-Control-Allow-Origin`. * If this has any value, it would also set `Access-Control-Allow-Headers` as `*`. * `undefined` means no CORS header. * @defaultValue `*` */ cors?: string; /** * Response header value of `Access-Control-Allow-Origin`. * @defaultValue `3600` */ corsMaxAge?: number; /** * Actual URL path is `${jsonHostPath}/${apiName}`. * For example, if `jsonHostPath` is `'/api'`, then you can send `POST /api/a/b/c/Test` to call API `a/b/c/Test`. * @defaultValue `'/'` */ jsonHostPath: string; } /** * A call request by `client.sendMsg()` * @typeParam Msg - Type of the message * @typeParam ServiceType - The same `ServiceType` to server, it is used for code auto hint. */ export declare abstract class MsgCall<Msg = any, ServiceType extends BaseServiceType = any> extends BaseCall<ServiceType> { readonly type: "msg"; readonly service: MsgService; readonly msg: Msg; constructor(options: MsgCallOptions<Msg, ServiceType>, logger?: PrefixLogger); } export declare class MsgCallHttp<Msg = any, ServiceType extends BaseServiceType = any> extends MsgCall<Msg, ServiceType> { readonly conn: HttpConnection<ServiceType>; constructor(options: MsgCallHttpOptions<Msg, ServiceType>); } export declare interface MsgCallHttpOptions<Msg, ServiceType extends BaseServiceType> extends MsgCallOptions<Msg, ServiceType> { conn: HttpConnection<ServiceType>; } export declare interface MsgCallOptions<Msg, ServiceType extends BaseServiceType> extends BaseCallOptions<ServiceType> { service: MsgService; msg: Msg; } export declare class MsgCallWs<Msg = any, ServiceType extends BaseServiceType = any> extends MsgCall<Msg, ServiceType> { readonly conn: WsConnection<ServiceType>; constructor(options: MsgCallWsOptions<Msg, ServiceType>); } export declare interface MsgCallWsOptions<Msg, ServiceType extends BaseServiceType> extends MsgCallOptions<Msg, ServiceType> { conn: WsConnection<ServiceType>; } export declare type MsgHandler<Call extends MsgCall = MsgCall> = (call: Call) => void | Promise<void>; /** * Auto add prefix using existed `Logger` */ export declare class PrefixLogger implements Logger { readonly logger: PrefixLoggerOptions["logger"]; readonly prefixs: PrefixLoggerOptions["prefixs"]; constructor(options: PrefixLoggerOptions); getPrefix(): string[]; log(...args: any[]): void; debug(...args: any[]): void; warn(...args: any[]): void; error(...args: any[]): void; } export declare interface PrefixLoggerOptions { logger: Logger; prefixs: (string | (() => string))[]; } export declare type SendReturnMethod<Res> = (ret: ApiReturn<Res>) => ReturnType<BaseConnection["sendData"]>; export declare enum ServerStatus { Opening = "OPENING", Opened = "OPENED", Closing = "CLOSING", Closed = "CLOSED" } /** * Print log to terminal, with color. */ export declare class TerminalColorLogger implements Logger { options: TerminalColorLoggerOptions; private _pid; constructor(options?: Partial<TerminalColorLoggerOptions>); private _time; debug(...args: any[]): void; log(...args: any[]): void; warn(...args: any[]): void; error(...args: any[]): void; } export declare interface TerminalColorLoggerOptions { /** * Process ID prefix * @defaultValue `process.pid` */ pid: string; /** * `undefined` represents not print time * @defaultValue 'yyyy-MM-dd hh:mm:ss' */ timeFormat?: string; } /** Version of TSRPC */ export declare const TSRPC_VERSION = "__TSRPC_VERSION__"; /** * Client for TSRPC WebSocket Server. * @typeParam ServiceType - `ServiceType` from generated `proto.ts` */ export declare class WsClient<ServiceType extends BaseServiceType> extends BaseWsClient<ServiceType> { readonly options: Readonly<WsClientOptions>; constructor(proto: ServiceProto<ServiceType>, options?: Partial<WsClientOptions>); } export declare interface WsClientOptions extends BaseWsClientOptions { } /** * Connected client */ export declare class WsConnection<ServiceType extends BaseServiceType = any> extends BaseConnection<ServiceType> { readonly type = "LONG"; protected readonly ApiCallClass: typeof ApiCallWs; protected readonly MsgCallClass: typeof MsgCallWs; readonly ws: WebSocket_2; readonly httpReq: http_2.IncomingMessage; readonly server: WsServer<ServiceType>; dataType: "text" | "buffer"; isDataTypeConfirmed?: boolean; constructor(options: WsConnectionOptions<ServiceType>); private _lastHeartbeatTime; private _heartbeatInterval?; get status(): ConnectionStatus; protected doSendData(data: string | Uint8Array, call?: ApiCallWs): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; }>; protected _rsClose?: () => void; /** Close WebSocket connection */ close(reason?: string, code?: number, closeTimeout?: number): Promise<void>; } export declare interface WsConnectionOptions<ServiceType extends BaseServiceType> extends BaseConnectionOptions<ServiceType> { server: WsServer<ServiceType>; ws: WebSocket_2; httpReq: http_2.IncomingMessage; onClose: (conn: WsConnection<ServiceType>, code: number, reason: string) => Promise<void>; dataType: "text" | "buffer"; isDataTypeConfirmed?: boolean; } /** * TSRPC Server, based on WebSocket connection. * It can support realtime cases. * @typeParam ServiceType - `ServiceType` from generated `proto.ts` */ export declare class WsServer<ServiceType extends BaseServiceType = any> extends BaseServer<ServiceType> { readonly options: WsServerOptions<ServiceType>; readonly connections: WsConnection<ServiceType>[]; private readonly _id2Conn; constructor(proto: ServiceProto<ServiceType>, options?: Partial<WsServerOptions<ServiceType>>); private _wsServer?; private _httpServer?; /** * {@inheritDoc BaseServer.start} */ start(): Promise<void>; /** * {@inheritDoc BaseServer.stop} */ stop(): Promise<void>; private _onClientConnect; private _onClientClose; /** * Send the same message to many connections. * No matter how many target connections are, the message would be only encoded once. * @param msgName * @param msg - Message body * @param connIds - `id` of target connections, `undefined` means broadcast to every connections. * @returns Send result, `isSucc: true` means the message buffer is sent to kernel, not represents the clients received. */ broadcastMsg<T extends string & keyof ServiceType["msg"]>(msgName: T, msg: ServiceType["msg"][T], conns?: WsConnection<ServiceType>[]): Promise<{ isSucc: true; } | { isSucc: false; errMsg: string; }>; } export declare interface WsServerOptions<ServiceType extends BaseServiceType> extends BaseServerOptions<ServiceType> { /** Which port the WebSocket server is listen to */ port: number; /** Whether to print `[Connected]` and `[Disconnected]` into log */ logConnect: boolean; /** * HTTPS options, the server would use wss instead of http if this value is defined. * NOTICE: Once you enabled wss, you CANNOT visit the server via `ws://` anymore. * If you need visit the server via both `ws://` and `wss://`, you can start 2 HttpServer (one with `wss` and another without). * @defaultValue `undefined` */ wss?: { /** * @example * fs.readFileSync('xxx-key.pem'); */ key: https.ServerOptions["key"]; /** * @example * fs.readFileSync('xxx-cert.pem'); */ cert: https.ServerOptions["cert"]; }; /** * Close a connection if not receive heartbeat after the time (ms). * This value should be greater than `client.heartbeat.interval`, for exmaple 2x of it. * `undefined` or `0` represent disable this feature. * @defaultValue `undefined` */ heartbeatWaitTime?: number; /** * WebSocket server options, transfer to 'ws' package. */ wsOptions?: WebSocket_2.ServerOptions; } export * from "tsrpc-base-client"; export * from "tsrpc-proto"; export { }