pubst
v0.7.0
Published
A slightly opinionated pub/sub event emitter for JavaScript.
Maintainers
Readme
Pubst
Pubst is a slightly opinionated pub/sub library for JavaScript.
Each Pubst instance acts as a single broker for any number of topics. Any number of modules sharing a single instance of Pubst can exchange data through it's publish and subscribe mechanisms
Pubst has a few other features worth noting:
Subscribers can register a default value to receive in the event that their topic does not have a value set or has a value of
null.By default, subscribers are primed with the last value published on the topic (or their default).
By default, subscribers will not receive subsequent calls when the same value is published to a topic more than once.
A subscriber can listen for updates to a given topic, or can provide a matcher function that determines which topics the subscriber should receive updates for.
Pubst supports pluggable store implementations for custom persistence strategies.
Breaking Changes (v0.7.0)
subscribeno longer acceptsRegExpas the first argument. Use a matcher function instead. A matcher function receives a topic name string and should return a truthy value if the subscriber should receive updates for that topic. If the matcher throws an error, the error is logged as a warning and the match is skipped.
Migrating from RegExp to matcher functions
// Before (v0.6.0) - RegExp
pubst.subscribe(/SELECTED\..*/, handler);
// After (v0.7.0) - Wrap the regex in a function
pubst.subscribe(t => /SELECTED\..*/.test(t), handler);
// Or, take advantage of more expressive matching logic:
pubst.subscribe(t => t.startsWith('SELECTED.'), handler);Breaking Changes (v0.6.0)
The constructor no longer accepts configuration or calls
configure(). The instance is ready to use immediately with default settings. Callawait configure()if you need to customize the logger, store, or pre-register topics.Most methods are now async.
configure,addTopic,addTopics,publish,currentVal,clear, andclearAllnow return Promises and can beawait-ed.subscriberemains synchronous and continues to return an unsubscribe function immediately. Priming of subscribers with existing values happens asynchronously in the background.
Basic Usage
While it isn't necessarily required, it is a good idea to configure your Pubst instance and the topics you will use in a single file and then export the instance so that other modules can consume them. This keeps your configuration and available topics together.
// pubst-broker.js
import Pubst from 'pubst';
const pubst = new Pubst();
export async function init() {
await pubst.configure({
topics: [
{
name: 'FAVORITE_COLOR',
doPrime: false
}
]
});
}
export { pubst };// File1.js
import { pubst } from './pubst-broker.js';
await pubst.addTopic({
name: 'NAME',
default: 'World'
});// File2.js
import { pubst } from './pubst-broker.js';
pubst.subscribe('NAME', name => {
console.log(`Hello ${name}!`);
});
Because the topic has a default value of 'World' and the topic is currently empty, the subscriber is primed with the default value.
Hello World!When another module publishes 'Jill' on the 'NAME' topic, the subscriber is called with the new value.
// File3.js
import { pubst } from './pubst-broker.js';
await pubst.publish('NAME', 'Jill');Hello Jill!If the topic is cleared, the subscriber is again called with their default value.
await pubst.clear('NAME');Hello World!A subscriber is free to override the default value for their topics.
Browser Usage
Browser-ready bundles are included in the dist/browser/ directory of the npm package. Include the script via a <script> tag and window.pubst will be available as a pre-instantiated Pubst instance.
<script src="pubst-browser-0.7.0.min.js"></script>
<script>
(async () => {
await pubst.configure({showWarnings: false});
pubst.subscribe('my.topic', value => {
console.log('Received:', value);
});
await pubst.publish('my.topic', 'hello');
})();
</script>Note: If you need to customize the instance (e.g. suppress warnings, provide a custom store, or pre-register topics), call await pubst.configure(...) before use. Otherwise, the instance is ready to use immediately.
Both unminified (pubst-browser-{version}.js) and minified (pubst-browser-{version}.min.js) builds are provided, each with a corresponding source map.
TypeScript
TypeScript type declarations are generated from JSDoc and included in the package. They are picked up automatically when importing Pubst:
import Pubst from 'pubst';
const pubst = new Pubst();
await pubst.configure({ showWarnings: false });
pubst.subscribe('my.topic', (value: unknown, topic: string) => {
console.log(topic, value);
});API
async configure(configOptions)
Sets Pubst configuration. Must be called after construction.
Available options:
showWarnings(default: true) - Show warnings in the console.logger(default: ConsoleLogger) - A custom logger to send warning messages to. If provided,showWarningsis ignored.store(default: InMemoryStore) - A custom store implementation for persisting topic values. See Custom Stores for details.topics- An array of topic configurations.
Example
const pubst = new Pubst();
await pubst.configure({
showWarnings: false,
topics: [
{
name: 'user.basicInfo',
default: {
lastName: 'No User Logged In',
firstName: 'No User Logged In'
}
}
]
});async addTopic(topicConfig) or async addTopics(topicConfigArrays)
Sets the configuration for a new topic. Registers the topic in the store.
Available options:
name(REQUIRED) - A string representing the name of the topic.default(default: undefined) - The default value presented to subscribers when the topic is undefined or null.- This can be overridden by subscribers.
eventOnly(default: false) - Set this totrueif this topic will not have payload data.doPrime(default: true) - Should new subscribers automatically receive the last published value on the topic?- This can be overridden by subscribers.
allowRepeats(default: false) - Alert subscribers of all publish events, even if the value is equal (by strict comparison) to the last value sent.- This can be overridden by subscribers.
storeConfig(default: {}) - Store-specific configuration that is passed through to the store'sregisterTopicmethod. This allows custom store implementations to receive topic-level configuration. The InMemoryStore ignores this value.
Examples
await pubst.addTopics([
{
name: 'game.started',
default: false,
doPrime: true
},
{
name: 'player.guess',
default: -1,
allowRepeats: true,
doPrime: false
},
{
name: 'player.won',
eventOnly: true,
doPrime: false
}
]);
await pubst.addTopic({
name: 'player.name',
default: 'Player 1'
});async publish(topic, payload)
Publishes a value to a topic. Topic names are expected to be strings. Payloads should be treated as immutable.
While publish is async, its resolution is not based on all subscribers being called. It resolves when the value to publish has been safely stored. For performance reasons, calling subscribers remains an asynchronous action.
Examples
await pubst.publish('SELECTED.COLOR', 'blue');
await pubst.publish('current.user', {
name: 'Some User',
permissions: [
'BORING.THINGS.VIEWER'
]
});NOTE: Mutating payloads received by a subscriber is a really bad idea. You may be changing data that other portions of your application are using. This is likely to result in terrible bugs that are difficult to find.
subscribe(topic, handler|subscriptionConfig[, defaultValue])
Registers a subscriber to one or more topics. This method is synchronous and returns an unsubscribe function immediately.
The first argument may be a string or a matcher function. If a string is provided, the handler will be called for all updates for that topic. If a function is provided, it will be called with each topic name and should return a truthy value to indicate the subscriber wishes to receive updates for that topic. If the matcher function throws an error, the error is logged as a warning and the match is skipped.
The second argument may be a handler function that is called when updates are published to the topic, or a configuration object for the subscription. The configuration object is necessary if you want to change default configuration options for this subscription.
Available options are:
handler- (Required) - The handler to call when the topic is updated.default- (Default: undefined) - Default value for this sub.doPrime- (Default: true) - Should the handler be primed with the last value?allowRepeats- (Default: false) - Should the handler be called when the value doesn't change?
The handler will be called on topic updates. It will be passed the new value of the topic as the first argument, and the name of the topic as the second argument.
Note: Priming of subscribers with existing store values happens asynchronously. The handler will be called after the store read resolves.
Example 1 - Basic usage
pubst.subscribe(
'SELECTED.COLOR', // The topic you are subscribing to
color => { // A handler function
doSomethingWithColor(color);
},
'red' // Default value
);Example 2 - Matcher function for multiple topics
pubst.subscribe(
t => t.startsWith('SELECTED.'), // Matches all topics starting with 'SELECTED.'
(payload, topic) => { // A handler function
if (topic === 'SELECTED.COLOR') {
doSomethingWithColor(payload);
} else if (topic === 'SELECTED.FOOD') {
doSomethingWithFood(payload);
}
},
'EMPTY' // Default value
);Example 3 - Matcher function using a regex
pubst.subscribe(
t => /^user\..*\.updated$/.test(t), // Use regex inside a matcher
(payload, topic) => {
handleUserUpdate(payload, topic);
}
);Example 4 - Subscription Configuration
pubst.subscribe(
'SELECTED.COLOR', // The topic you are subscribing to
{
// The handler
handler: color => (doSomethingWithColor(color)),
// Default value
default: 'red',
// Do not prime the handler with the last value on subscribe
doPrime: false,
// Call the handler even when published value did not change
allowRepeats: true
}
);async currentVal(topic[, defaultValue])
Gets the current value of a topic.
If a defaultValue is provided and the topic is currently undefined or null, the defaultValue will be returned.
Example
const color = await pubst.currentVal('SELECTED.COLOR', 'red');async clear(topic)
Clears a given topic by publishing a null to it.
Subscribers that provided a default value will receive their default.
Example
await pubst.clear('SELECTED.COLOR');async clearAll()
Clears all known topics.
Example
await pubst.clearAll();Custom Stores
Pubst uses an in-memory store by default, but you can provide your own store implementation for custom persistence strategies (e.g. localStorage, IndexedDB, a remote API, etc.).
A custom store must implement the following async methods:
| Method | Description |
|-----------------------------------------------------|------------------------------------------------|
| registerTopic(topicName, initialVal, storeConfig) | Called when a topic is configured via addTopic. initialVal is null for new topics. storeConfig is the topic-level store configuration passed through from the topic's storeConfig option. |
| getValue(topicName) | Retrieve the current value for a topic. |
| setValue(topicName, value) | Store a new value for a topic. |
| clearValue(topicName) | Clear the value for a topic (set to null). |
| getTopicNames() | Return an array of all registered topic names. |
All methods must return a Promise (or be declared async).
The built-in InMemoryStore class serves as the reference implementation.
