import { ChatClient, ClientOptions, Connection, Logger, Rooms } from '@ably/chat'; import { Realtime, ClientOptions as RealtimeClientOptions } from 'ably'; import { jsonRpcBlocking } from './jsonrpc'; import { RpcRealtime } from './pubsub'; import { RpcConnection } from './RpcConnection'; import { RpcLogger } from './RpcLogger'; import { RpcRooms } from './RpcRooms'; import { transformRealtimeClientOptions } from './utils'; // chat-js SDK currently does not provide an interface for its ChatClient, only the class. // using some bit of a TypeScript magic we can "implement" that class as if it was an interface. // however we encounter issues with private properties in this case: TypeScript wants us to define them // in the child class, but when doing so it shows an error because now we have two separate declarations of a private property // in the class hierarchy. one possible workaround is to extract only public members of the ChatClient with a mapped type below. // see https://stackoverflow.com/a/48953930 // alternatively, chat-js may expose an interface for public members of ChatClient type PublicPart = { [K in keyof T]: T[K] }; export type PublicChatClient = PublicPart; export class RpcChatClient implements PublicChatClient { private readonly _instanceId: string; private _rooms: RpcRooms | undefined; private _connection: RpcConnection | undefined; private _logger: RpcLogger | undefined; /** * We are passing realtime client options instead of the realtime client instance, since instance can't be serialized over the wire. * It is adapter's responsibility to then construct a corresponding realtime client using provided options. */ constructor(realtimeClientOptions: RealtimeClientOptions, clientOptions?: ClientOptions) { const { refId } = jsonRpcBlocking.request('ChatClient', { args: { realtimeClientOptions: realtimeClientOptions ? transformRealtimeClientOptions(realtimeClientOptions) : undefined, clientOptions: clientOptions ? clientOptions : undefined, }, }); this._instanceId = refId; } get rooms(): Rooms { if (!this._rooms) { const { refId } = jsonRpcBlocking.request('ChatClient#rooms', { refId: this._instanceId }); this._rooms = new RpcRooms(refId); } return this._rooms; } get connection(): Connection { if (!this._connection) { const { refId } = jsonRpcBlocking.request('ChatClient#connection', { refId: this._instanceId }); this._connection = new RpcConnection(refId); } return this._connection; } get clientId(): string { const { response } = jsonRpcBlocking.request('ChatClient#clientId', { refId: this._instanceId }); return response; } get realtime(): Realtime { const { refId } = jsonRpcBlocking.request('ChatClient#realtime', { refId: this._instanceId, }); return new RpcRealtime(refId); } get clientOptions(): ClientOptions { // temporary solution: client options are not fully serializable (they contain callback functions), // so they will be missing such parts when returned over RPC. const { response } = jsonRpcBlocking.request('ChatClient#clientOptions', { refId: this._instanceId }); return response; } get logger(): Logger { const { refId } = jsonRpcBlocking.request('ChatClient#logger', { refId: this._instanceId, }); return new RpcLogger(refId); } addReactAgent(): void { jsonRpcBlocking.request('ChatClient.addReactAgent', { refId: this._instanceId }); } }