Pubkeeper Client
@pubkeeper/client
enables web browsers to join, consume, and produce content on a Pubkeeper network.
Concepts & Terms
-
Brew
: A protocol/method of delivery, e.g. WebSocket, SocketIO, WebRTC, HTTP -
Brewer
: An object bound to a specific topic that accepts a data transports it to matched patrons. -
Patron
: A consumer bound to one or more topics, it receives data from matched brewers.
Installation
In a browser:
<script src="pubkeeper-client.js"></script>
Using npm:
$ npm install --save @pubkeeper/client
Then, load using ES5 require()
syntax…
var PubkeeperClient = require('@pubkeeper/client').PubkeeperClient;
…or with ES2015+ import
syntax:
import { PubkeeperClient } from '@pubkeeper/client';
Basic Syntax
Client Configuration
import { PubkeeperClient } from '@pubkeeper/client';
import { WebSocketBrew } from '@pubkeeper/brew-websocket';
import { PubkeeperAesCbcCrypto } from '@pubkeeper/crypto-cjs-aes-cbc';
// Create a client
const client = new PubkeeperClient({
server: 'wss://192.168.100.191:9898/ws',
jwt: "[your JWT]",
crypto: new Map([
PubkeeperAesCbcCrypto,
]),
brews: [
new WebSocketBrew({
brewerConfig: {
hostname: '192.168.100.191',
port: 8000,
secure: true,
},
}),
],
});
client.connect();
Note: This client is the core pubkeeper client, as such it does not include any crypto or brews. If you want a pre-packaged version suitable for general usage see @pubkeeper/browser-client
Brewing Setup
To produce data into the Pubkeeper network you will need to create a brewer. The example below will setup a Brewer with the topic of topic.information
client.addBrewer('topic.information', (brewer) => {
// brewer has been *matched* by pubkeeper, i.e. there is
// at least one patron of this topic.
let counter = 0;
const id = setInterval(() => {
brewer.brewJSON({ counter });
counter += 1;
}, 1000);
return function teardown() {
// teardown function will be called when there are
// _no_ more patrons or this brewer has been removed.
clearInterval(id);
};
});
Patron Setup
To listen to someone who is brewing information, you need to subscribe to the topic, and configure a callback to occur when there is data available for processing.
client.addPatron('weather.*', (patron) => {
// patron has been *matched* by pubkeeper, i.e. there is
// at least one brewer of this topic.
function handler(bytes, meta) {
console.log(meta.from, meta.topic, patron.id, bytes);
};
patron.on('message', handler);
return function teardown() {
// teardown function will be called when there are
// _no_ more brewers or this patron has been removed.
patron.off('message', handler);
};
});
Brewer/Patron Connections
The Patron
message
event is always fired with a Uint8Array
as data
, no matter which brew*()
method was called on the brewing side. You need to handle parsing/casting those bytes.
For instance, given a brewer:
client.addBrewer('tick', (b) => {
let id, count = 0;
(function tick() {
id = setTimeout(tick, 1000);
b.brewJSON({ count });
})();
return () => { clearTimeout(id); };
});
Then on the patron side, it would look like this;
client.addPatron('tick', (p) => {
const handler = (bytes) => {
const text = new TextDecoder().decode(bytes);
const value = JSON.parse(text);
console.log(value.count);
};
p.on('message', handler);
return () => { p.off('message', handler); };
});
NOTE This will be resolved in future versions of the protocol.
State Change Events
Brewers and Patrons will be notified when thier couterparts are added or removed:
client.addBrewer('topic.information', (brewer) => {
const handleAdded = ({ id, topic }) => {
console.log('patron (%s) added', id);
};
const handleRemoved = ({ id }) => {
console.log('patron (%s) removed', id);
};
// setup
brewer.on('added', handleAdded);
brewer.on('removed', handleRemoved);
return function teardown() {
// cleanup
brewer.off('added', handleAdded);
brewer.off('removed', handleRemoved);
};
});
PubkeeperClient
Class
The PubkeeperClient
manages the connection to a pubkeeper server.
new PubkeeperClient(options)
Name | Type | Description |
---|---|---|
options |
ClientOptions |
The options for this pubkeeper client |
ClientOptions
: object
These options can be given to a PubkeeperClient to customize its behavior.
Name | Type | Default | Description |
---|---|---|---|
server |
string |
- | Websocket URL of the pubkeeper server |
jwt |
string |
- | Auth token for the pubkeeper server |
brews |
Array<Brew> |
[] |
Supported brews of this client |
crypto |
Map<*,Crypto> |
new Map |
Supported crypto mappings for this client |
protocols |
Map<*,Protocol> |
new Map |
Supported protocol mappings for this client. Defaults to the legacy protocol |
maxRetries |
number |
Infinity |
Maximum number of connection retries |
maxTimeout |
number |
10000 |
Maximum timeout between retries (in milliseconds) |
Examples
Using ES2015 Promise
:
const client = new PubkeeperClient(config)
client.connect().then(() => {
console.log('ready');
});
Using ES2017 async
/await
:
(async () => {
const client = new PubkeeperClient(config)
await client.connect();
console.log('ready');
})();
Members
.status
: string
Returns the status of this client. Can be one of the following:
Value | Description |
---|---|
'connecting' |
Attempting to connect. |
'connected' |
Connected |
'reconnecting' |
Has lost the connection, and has queued a reconnection attempt. |
'disconnected' |
Disconnected |
'dead' |
Given up retrying to connect |
Methods
.connect({ wait = true } = {})
: Promise?
Connect to the configured pubkeeper server. If { wait: true }
is specified, this function will return a Promise
that will be resolved when the connection to the pubkeeper server has been finalized. Passing { wait: false }
will immediately return undefined
and the server may or may not be connected yet.
.disconnect()
Disconnect from the server.
.addBrewer(topic[,[ options,] matched])
: Brewer
Create a new brewer of topic
.
Name | Type | Description |
---|---|---|
topic |
string |
The topic |
options |
BrewerOptions |
|
matched |
Function |
Callback that is invoked when the brewer has been matched with a patron |
BrewerOptions
: object
These options can be given to the PubkeeperClient#addBrewer
method to customize the brewers behavior.
Name | Type | Default | Description |
---|---|---|---|
autoRemoveListeners |
boolean |
false | Automatically remove all event listeners on unmatch |
brews |
Array<(string|Brew)> |
- | A subset of brews to use for this brewer, defaults to all registered brews. |
id |
string |
- | The UUID of this brewer, defaults to a random UUIDv4 |
waitForBrews |
(number|boolean) |
250 | The maximum wait time to wait when brewing data for brew connections to stablize. |
Examples
Using the matched
callback:
client.addBrewer('topic', (brewer) => {
brewer.brewText('somebody is out there!');
});
With extended options:
// Do not wait for brew connections to stablize before sending.
client.addBrewer(
'topic',
{ waitForBrews: false },
(brewer) => {
brewer.brewText('somebody is out there!');
},
);
Managing resources with teardown
function:
client.addBrewer(
'device.motion',
(brewer) => {
const handler = ({ acceleration }) => {
brewer.brewJSON(acceleration);
};
window.addEventListener('devicemotion', handler);
return () => {
window.removeEventListener('devicemotion', handler);
};
},
);
.removeBrewer(brewer)
Remove a brewer from this client.
Examples
const ref = client.addBrewer('topic', matchedFn);
/* ...later */
client.removeBrewer(ref);
.addPatron(topic[,[ options,] matched])
: Patron
Create a new patron of topic
.
Name | Type | Description |
---|---|---|
topic |
string |
The topic |
options |
PatronOptions |
|
matched |
Function |
Callback that is invoked when the patron has been matched with a brewer |
PatronOptions
: object
These options can be given to the PubkeeperClient#addPatron
method to customize the patrons behavior.
Name | Type | Default | Description |
---|---|---|---|
autoRemoveListeners |
boolean |
false | Automatically remove all event listeners on unmatch |
brews |
Array<(string|Brew)> |
- | A subset of brews to use for this patron, defaults to all registered brews. |
id |
string |
- | The UUID of this patron, defaults to a random UUIDv4 |
Examples
Using the matched
callback:
client.addBrewer('topic', (patron) => {
const handler = (data, meta) => { /* process data */ };
patron.on('message', hanlder);
return () => { patron.off('message', handler); };
});
.removePatron(brewer)
Remove a patron from this client.
Examples
const ref = client.addPatron('topic', matchedFn);
/* ...later */
client.removePatron(ref);
Brewer
Class
A brewer is a publisher of information in a pubkeeper system.
Members
.id
: string
the uuid of this brewer
.brews
: Array<string>?
the subset of brew names that this brewer is using.
.topic
: string
the topic this brewer is bound to
.count
: number
current count of matched patrons
.isMatched
: boolean
Is the brewer currently matched with any patrons
Methods
.brew(bytes[, options])
: Promise
Publish binary data to a brewer.
Name | Type | Description |
---|---|---|
bytes |
(Uint8Array|Uint8ClampedArray) |
Binary data to brew |
options |
object |
|
options.waitForBrews |
number? |
Override the brewers config |
.brewText(text[, options])
: Promise
Publish text
as a UTF-8 encoded byte array.
Name | Type | Description |
---|---|---|
text |
string |
text to brew |
options |
object |
|
options.waitForBrews |
number? |
Override the brewers config |
.brewJSON(value[, options])
: Promise
Publish value
using JSON.stringify
.
Name | Type | Description |
---|---|---|
value |
* |
value to serialize and brew |
options |
object |
|
options.waitForBrews |
number? |
Override the brewers config |
.on(event, fn[, context])
: this
.addListener(event, fn[, context])
: this
Add a listener for a given event.
.off(event, fn[, context[, once]])
: this
.removeListener(event, fn[, context[, once]])
: this
Remove the listeners of a given event.
Events
added
Fired when a matched patron is added.
Name | Type | Description |
---|---|---|
e |
object |
|
e.id |
string |
UUID of the added patron |
e.topic |
string |
topic of the brewer |
removed
Fired when a matched patron is removed.
Name | Type | Description |
---|---|---|
e |
object |
|
e.id |
string |
UUID of the removed patron |
Patron
Class
A patron is a subscriber of information in a pubkeeper system.
Members
.id
: string
the uuid of this patron
.brews
: Array<string>?
the subset of brew names that this patron is using.
.topic
: string
the topic this patron is bound to
.count
: number
current count of matched brewers
.isMatched
: boolean
Is the patron currently matched with any brewers
Methods
.on(event, fn[, context])
: this
.addListener(event, fn[, context])
: this
Add a listener for a given event.
.off(event, fn[, context[, once]])
: this
.removeListener(event, fn[, context[, once]])
: this
Remove the listeners of a given event.
Events
added
Fired when a matched brewer is added.
Name | Type | Description |
---|---|---|
e |
object |
|
e.id |
string |
UUID of the added brewer |
e.topic |
string |
topic of the brewer |
removed
Fired when a matched brewer is removed.
Name | Type | Description |
---|---|---|
e |
object |
|
e.id |
string |
UUID of the removed brewer |
message
Fired when a message is recieved from a brewer.
Name | Type | Description |
---|---|---|
data |
Uint8Array |
The binary data from the brewer |
meta |
object |
|
meta.topic |
string |
topic of the brewer that brewed this data |
meta.from |
string |
UUID of the brewer that brewed this data |