@akala/json-rpc-ws
Version:
json-rpc websocket transport
136 lines (114 loc) • 3.6 kB
text/typescript
;
import debug from 'debug';
const logger = debug('akala:json-rpc-ws');
import { Connection, type Handler, type PayloadDataType, type Parent, Payload } from './shared-connection.js';
import type { SocketAdapter } from '@akala/core';
/**
* Base functionality shared by client and server
*
* @constructor
* @public
*/
export abstract class Base<TStreamable, TConnection extends Connection<TStreamable> = Connection<TStreamable>> implements Parent<TStreamable, TConnection>
{
constructor(public type: string)
{
}
public id = crypto.randomUUID();
public browser = false;
private requestHandlers: { [method: string]: Handler<TConnection, TStreamable, PayloadDataType<TStreamable>, PayloadDataType<TStreamable>> } = {};
protected connections: { [id: string | number]: Connection<TStreamable> } = {};
/**
* Add a handler function for a given method
*
* @param {String} method - name of the method to add handler for.
* @param {function} handler - function to be passed params for given method.
* @todo enforce handler w/ two-param callback
* @public
*/
public expose<TParamType extends PayloadDataType<TStreamable>, TReplyType extends PayloadDataType<TStreamable>>(method: string, handler: Handler<TConnection, TStreamable, TParamType, TReplyType>): void
{
logger('registering handler for %s', method);
if (this.requestHandlers[method])
{
throw Error('cannot expose handler, already exists ' + method);
}
this.requestHandlers[method] = handler;
}
/**
* Connected event handler
*
* @param {Object} socket - new socket connection
* @private
*/
public connected(socket: SocketAdapter<Payload<TStreamable>>): void
{
const connection = this.connection(socket);
logger('%s connected with id %s', this.type, connection.id);
this.connections[connection.id] = connection;
}
abstract connection(socket: SocketAdapter<Payload<TStreamable>>): Connection<TStreamable>;
/**
* Disconnected event handler
*
* @param {Object} connection - connection object that has been closed
* @private
*/
public disconnected(connection: Connection<TStreamable>): void
{
logger('disconnected');
delete this.connections[connection.id];
}
/**
* Test if a handler exists for a given method
*
* @param {String} method - name of method
* @returns {Boolean} whether or not there are any handlers for the given method
* @public
*/
public hasHandler(method: string): boolean
{
if (this.requestHandlers[method] !== undefined)
{
return true;
}
return false;
}
/**
* Get handler for a given method
*
* @param {String} method - name of method
* @returns {Array} - handler for given method
* @public
*/
public getHandler(method: string): Handler<TConnection, TStreamable, PayloadDataType<TStreamable>, PayloadDataType<TStreamable>>
{
return this.requestHandlers[method];
}
/**
* Get a connection by id
*
* @param {id} id - id of the connection to get
* @returns {Connection} - Connection
* @public
*/
public getConnection(id: string | number): Connection<TStreamable>
{
return this.connections[id];
}
/**
* Shut down all existing connections
*
* @public
*/
public hangup(): void
{
logger('hangup');
const connections = Object.keys(this.connections);
connections.forEach(function hangupConnection(this: Base<TStreamable, TConnection>, id)
{
this.connections[id].close();
delete this.connections[id];
}, this);
}
}