@akala/json-rpc-ws
Version:
json-rpc websocket transport
116 lines (103 loc) • 3.97 kB
text/typescript
import ws from 'ws';
import { SocketAdapter, SocketAdapterAkalaEventMap } from '../shared-connection.js';
import { AllEventKeys, AllEvents, EventArgs, EventKeys, EventListener, EventOptions, EventReturnType, StatefulSubscription, Subscription, TeardownManager } from '@akala/core';
/**
* json-rpc-ws connection
*
* @constructor
* @param {Socket} socket - web socket for this connection
* @param {Object} parent - parent that controls this connection
*/
export default class WsSocketAdapter extends TeardownManager implements SocketAdapter
{
constructor(private socket: ws)
{
super();
}
hasListener<const TKey extends AllEventKeys<SocketAdapterAkalaEventMap>>(name: TKey)
{
return !!this.socket.listenerCount(name);
}
get definedEvents(): AllEventKeys<SocketAdapterAkalaEventMap>[]
{
return ['message', 'close', 'error', 'open'].filter(k => this.socket.listenerCount(k)) as AllEventKeys<SocketAdapterAkalaEventMap>[]
}
emit<const TEvent extends EventKeys<SocketAdapterAkalaEventMap>>(event: TEvent, ...args: EventArgs<SocketAdapterAkalaEventMap[TEvent]>): false | EventReturnType<SocketAdapterAkalaEventMap[TEvent]>
{
throw new Error('Method not implemented.');
}
pipe(socket: SocketAdapter)
{
this.on('message', (message) => socket.send(message));
this.on('close', () => socket.close());
}
get open(): boolean
{
return this.socket.readyState == ws.OPEN;
}
close(): void
{
this.socket.close();
}
send(data: string): void
{
this.socket.send(data, { binary: false });
}
public off<const TEvent extends AllEventKeys<SocketAdapterAkalaEventMap>>(
event: TEvent,
handler: EventListener<AllEvents<SocketAdapterAkalaEventMap>[TEvent]>
): boolean
{
if (event === 'message')
{
this.socket.removeAllListeners(event);
}
else
this.socket.off(event, handler);
return true;
}
private readonly messageListeners: [(ev: unknown) => void, (data: ws.Data, isBinary: boolean) => void][] = [];
public on<const TEvent extends AllEventKeys<SocketAdapterAkalaEventMap>>(
event: TEvent,
handler: EventListener<AllEvents<SocketAdapterAkalaEventMap>[TEvent]>,
options?: EventOptions<AllEvents<SocketAdapterAkalaEventMap>[TEvent]>
): Subscription
{
if (event === 'message')
{
function x(data: ws.Data, isBinary: boolean)
{
if (!isBinary)
{
if (Buffer.isBuffer(data))
(handler as EventListener<SocketAdapterAkalaEventMap['message']>).call(this, data.toString('utf8'));
else
(handler as EventListener<SocketAdapterAkalaEventMap['message']>).call(this, data);
}
else
(handler as EventListener<SocketAdapterAkalaEventMap['message']>).call(this, data);
}
this.messageListeners.push([handler, x]);
if (options?.once)
this.socket.once(event, x);
else
this.socket.on(event, x);
return new StatefulSubscription(() => this.socket.off(event, x)).unsubscribe;
}
else
{
if (options?.once)
this.socket.on(event, handler);
else
this.socket.on(event, handler);
return new StatefulSubscription(() => this.socket.off(event, handler)).unsubscribe;
}
}
public once<const TEvent extends AllEventKeys<SocketAdapterAkalaEventMap>>(
event: TEvent,
handler: EventListener<AllEvents<SocketAdapterAkalaEventMap>[TEvent]>
): Subscription
{
return this.on(event, handler, { once: true } as EventOptions<AllEvents<SocketAdapterAkalaEventMap>[TEvent]>);
}
}