An RxJS message broker for WebRTC DataChannels and WebSockets.
This module is using the power of RxJS to wrap WebSockets or WebRTC DataChannels. It returns a Subject which can be used with all the operators that RxJS provides. But it also provides some additional functionality.
To install rxjs-broker
via npm you can run the following command.
npm install rxjs-broker
rxjs-broker
does provide two utility functions: connect()
and wrap()
. If you're using ES2015 modules you can import them like that.
import { connect, wrap } from 'rxjs-broker';
The connect()
function takes a URL as a parameter and returns a WebSocketSubject
which extends the AnonymousSubject
provided by RxJS. It also implements the IRemoteSubject
interface which adds two additional methods. It gets explained in more detail below.
const webSocketSubject = connect('wss://super-cool-websock.et');
The second parameter can be used to specify an openObserver
which works similar to the openObserver
of the WebSocketSubject
provided by RxJS. The next()
method of it gets called when the underlying WebSocket emits an open event.
wrap(dataChannel: DataChannel, subjectConfig?: { openObserver?: NextObserver<void> }): DataChannelSubject
The wrap()
function can be used to turn a WebRTC DataChannel into a DataChannelSubject
which does also extend the AnonymousSubject
and implements the IRemoteSubject
interface.
// Let's imagine a variable called dataChannel exists and its value is a WebRTC DataChannel.
const dataChannelSubject = wrap(dataChannel);
The second parameter can be used to specify an openObserver
. The next()
method of it gets called when the underlying DataChannel emits an open event.
As mentioned above the IRemoteSubject
interface is used to describe the common behavior of the DataChannelSubject
and the WebSocketSubject
. In TypeScript it looks like this:
interface IRemoteSubject<T> {
close(): void;
send(message: T): Promise<void>;
}
The close()
method is meant to close the underlying WebSocket or WebRTC DataChannel.
The send()
method is a supercharged version of next()
. It will stringify a given JSON message before sending it and returns a Promise
which resolves when the message is actually on it's way.
rxjs-broker
does also provide another standalone function called mask()
. It can be imported like that.
import { mask } from 'rxjs-broker';
The mask()
function takes a JSON object which gets used to extract incoming data and to enhance outgoing data. If there is for example a DataChannel which receives two types of messages (control messages and measurement messages), they might look somehow like this:
{
"type": "control",
"message": {
"heating": "off"
}
}
{
"type": "measurement",
"message": {
"temperature": "30°"
}
}
In case you are not interested in the messages of type control and only want to receive and send messages of type measurement, you can use mask()
to achieve exactly that.
const maskedSubject = mask({ type: 'measurement' }, dataChannelSubject);
// The callback will be called with unwrapped messages like { temperature: '30°' }.
maskedSubject.subscribe((message) => {
// ...
});
When you call next()
or send()
on the returned IRemoteSubject
it also wraps the message with the provided mask. Considering the example introduced above, the usage of the send()
method will look like this:
const maskedSubject = mask({ type: 'measurement' }, dataChannelSubject);
// This will send wrapped messages like { type: 'measurement', message: { temperature: '30°' } }.
maskedSubject.send({ temperature: '30°' });