Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pubst

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pubst - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0

281

lib/pubst.js

@@ -6,6 +6,5 @@ 'use strict';

/**
* Pubst - Basic JavaScript Pub/Sub Event Emitter
* @module pubst
* Pubst - A slightly opinionated pub/sub library for JavaScript.
*
* Copyright 2017 Jason Schindler
* Copyright 2017-2018 Jason Schindler
*

@@ -23,2 +22,8 @@ * Licensed under the Apache License, Version 2.0 (the "License");

* limitations under the License.
*
* @file pubst.js
* @module pubst
* @author Jason Schindler
* @copyright Jason Schindler 2017-2018
* @description A slightly opinionated pub/sub library for JavaScript.
*/

@@ -59,7 +64,15 @@

/**
* Set global configuration.
* @summary Set global configuration.
*
* @alias module:pubst.configure
* @param {Object} config - Your configuration
*
* @description
* <p>
* Available options are:
* `showWarnings` (default: true) - Show warnings in the console.
* <ul>
* <li>`showWarnings` (default: true) - Show warnings in the console.</li>
* <li>`topics` - An array of topic configurations. (See: `addTopic` for topic configuration options)</li>
* </ul>
* </p>
*/

@@ -74,2 +87,6 @@ function configure() {

}
if (Array.isArray(userConfig.topics)) {
addTopics(userConfig.topics);
}
}

@@ -80,3 +97,91 @@

var regexSubs = [];
var topics = {};
var DEFAULT_TOPIC_CONFIG = {
name: '',
default: undefined,
eventOnly: false,
doPrime: true,
allowRepeats: false
};
var ALLOWED_SUB_PROPS = ['topic', 'handler', 'default', 'doPrime', 'allowRepeats'];
function buildConfig(base, extensions) {
var result = {};
for (var key in base) {
result[key] = base[key];
}
for (var _key2 in extensions) {
if (result.hasOwnProperty(_key2)) {
result[_key2] = extensions[_key2];
}
}
return result;
}
/**
* @summary Configure a new topic.
*
* @alias module:pubst.addTopic
* @param {Object} newTopicConfig - Topic configuration
*
* @description
* <p>
* Allows you to configure a new topic in pubst.
*
* Available options are:
* <ul>
* <li>`name` (<strong>REQUIRED</strong>) - A string representing the name of the topic.</li>
* <li>
* `default` (default: undefined) - The default value presented to subscribers when the topic is undefined or null.
* This can be overridden by subscribers.
* </li>
* <li>
* `eventOnly` (default: false) - Set this to true if this topic will not have payload data.
* </li>
* <li>
* `doPrime` (default: true) - Should new subscribers automatically receive the last published value on the topic?
* If no valid value is present, new subscribers will be primed with the default value (if configured).
* This can be overridden by subscribers.
* </li>
* <li>
* `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.
* </li>
* </ul>
* </p>
*/
function addTopic(newTopicConfig) {
var topic = buildConfig(DEFAULT_TOPIC_CONFIG, newTopicConfig);
if (!topic.name) {
throw new Error('Topics must have a name.');
}
if (topics[topic.name]) {
warn('The \'' + topic.name + '\' topic has already been configured. The previous configuration will be overwritten.');
}
topics[topic.name] = topic;
}
/**
* @summary Configure new topics.
*
* @alias module:pubst.addTopics
* @param {Array<Object>} newTopicConfigs - Topic configurations
*
* @description
* <p>
* Allows you to configure new topics. This will call `addTopic` with each item passed.
* For available options, see `addTopic`.
*/
function addTopics(topics) {
topics.forEach(addTopic);
}
function getStringSubsFor(topic) {

@@ -94,4 +199,13 @@ return Array.isArray(stringSubs[topic]) ? stringSubs[topic] : [];

if (typeof subscriber.topic === 'string') {
if (!topics[subscriber.topic]) {
warn('Adding a subscriber to non-configured topic \'' + subscriber.topic + '\'');
}
stringSubs[subscriber.topic] = getStringSubsFor(subscriber.topic).concat(subscriber);
} else if (subscriber.topic instanceof RegExp) {
var matchCount = Object.keys(topics).filter(function (topic) {
return topic.match(subscriber.topic);
}).length;
if (matchCount === 0) {
warn('Adding a RegExp subscriber that matches no configured topics.');
}
regexSubs.push(subscriber);

@@ -119,4 +233,12 @@ } else {

function isUndefined(input) {
return typeof input === 'undefined';
}
function isDefined(input) {
return !isUndefined(input);
}
function isNotSet(item) {
return item === null || typeof item === 'undefined';
return item === null || isUndefined(item);
}

@@ -129,3 +251,3 @@

function valueOrDefault(value, def) {
if (isNotSet(value) && typeof def !== 'undefined') {
if (isNotSet(value) && isDefined(def)) {
return def;

@@ -137,15 +259,27 @@ }

function getTopicConfig(topic) {
return topics[topic] || buildConfig(DEFAULT_TOPIC_CONFIG, { name: topic });
}
function scheduleCall(sub, payload, topic) {
setTimeout(function () {
var value = valueOrDefault(payload, sub.default);
if (sub.allowRepeats || sub.lastVal !== value || sub.lastTopic !== topic) {
var topicConfig = getTopicConfig(topic);
var defVal = typeof sub.default === 'undefined' ? topicConfig.default : sub.default;
var eventOnly = sub.hasOwnProperty('eventOnly') ? sub.eventOnly : topicConfig.eventOnly;
var allowRepeats = sub.hasOwnProperty('allowRepeats') ? sub.allowRepeats : topicConfig.allowRepeats;
var value = eventOnly ? topic : valueOrDefault(payload, defVal);
if (eventOnly || allowRepeats || sub.lastVal !== value || sub.lastTopic !== topic) {
setTimeout(function () {
sub.handler(value, topic);
sub.lastVal = value;
sub.lastTopic = topic;
}
}, 0);
}, 0);
}
}
/**
* Publish to a topic
* @summary Publish to a topic
*
* @alias module:pubst.publish
* @param {string} topic - The topic to publish to

@@ -155,2 +289,6 @@ * @param {*} payload The payload to publish

function publish(topic, payload) {
if (!topics[topic]) {
warn('Received a publish for ' + topic + ' but that topic has not been configured.');
}
store[topic] = payload;

@@ -169,5 +307,8 @@ var subs = allSubsFor(topic);

/**
* Subscribe to one or more topics
* @summary Subscribe to one or more topics
*
* @alias module:pubst.subscribe
* @param {string|RegExp} topic - The topic to receive updates for
* @param {Function|Object} handler
* @param {Function|Object} handler - Either your handler function or
* a subscription configuration object
* @param {*} default - (Optional) Value to send when topic is empty

@@ -178,2 +319,4 @@ *

*
* @description
* <p>
* The first argument may be a string or a regular expression.

@@ -184,36 +327,38 @@ * If a string is provided, the handler will be called for all

* expression.
* </p>
*
* <p>
* The second argument may be a function or an object. The object
* is necessary if you want to provide configuration options for
* this subscription. Available options are:
* <ul>
* <li>`default` - (Default: undefined) - Default value for this sub.</li>
* <li>`doPrime` - (Default: true) - Should the handler be primed with
* the last value?</li>
* <li>`allowRepeats` - (Default: false) - Should the handler be called
* when the value doesn't change?</li>
* <li>`handler` - (Required) - The handler to call.</li>
* </ul>
* </p>
*
* `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
* `handler` - (Required) - The handler to call.
*
* <p>
* 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.
* </p>
*/
function subscribe(topic, handler, def) {
var subscription = {
topic: topic,
default: undefined,
doPrime: true,
allowRepeats: false,
handler: function handler() {}
};
var subscription = void 0;
if (typeof handler === 'function') {
subscription.default = def;
subscription.handler = handler;
subscription = { topic: topic, default: def, handler: handler };
} else if ((typeof handler === 'undefined' ? 'undefined' : _typeof(handler)) === 'object') {
for (var key in handler) {
if (subscription.hasOwnProperty(key)) {
subscription[key] = handler[key];
}
}
subscription = {};
Object.keys(handler).filter(function (key) {
return ALLOWED_SUB_PROPS.includes(key);
}).forEach(function (key) {
subscription[key] = handler[key];
});
subscription.topic = topic;
}

@@ -223,28 +368,29 @@

if (subscription.doPrime) {
var stored = void 0;
var stored = void 0;
if (typeof topic === 'string') {
stored = [{
topic: topic,
val: currentVal(topic, def)
}];
} else if (topic instanceof RegExp) {
stored = Object.keys(store).filter(function (key) {
return key.match(topic);
}).map(function (key) {
return {
topic: key,
val: currentVal(key, def)
};
});
}
stored.forEach(function (item) {
if (isSet(item.val)) {
scheduleCall(subscription, item.val, item.topic);
}
if (typeof topic === 'string') {
stored = [{
topic: topic,
val: currentVal(topic, def)
}];
} else if (topic instanceof RegExp) {
stored = Object.keys(store).filter(function (key) {
return key.match(topic);
}).map(function (key) {
return {
topic: key,
val: currentVal(key, def)
};
});
}
stored.forEach(function (item) {
var topicConfig = getTopicConfig(item.topic);
var doPrime = subscription.hasOwnProperty('doPrime') ? subscription.doPrime : topicConfig.doPrime;
if (doPrime && (topicConfig.eventOnly || isSet(item.val))) {
scheduleCall(subscription, item.val, item.topic);
}
});
return function () {

@@ -256,3 +402,5 @@ removeSub(subscription);

/**
* Get the current value of a topic.
* @summary Get the current value of a topic.
*
* @alias module:pubst.currentVal
* @param {string} topic - The topic to get the value of.

@@ -264,10 +412,13 @@ * @param {*} default - (Optional) a value to return if the topic is

function currentVal(topic, def) {
return valueOrDefault(store[topic], def);
var defToUse = isDefined(def) ? def : getTopicConfig(topic).default;
return valueOrDefault(store[topic], defToUse);
}
/**
* Clears a given topic.
* @summary Clears a given topic.
*
* @alias module:pubst.clear
* @param {string} topic - The topic to clear
*
* Clears the topic by publishing a `null` to it.
* @description Clears the topic by publishing a `null` to it.
*/

@@ -281,3 +432,5 @@ function clear(topic) {

/**
* Clears all known topics.
* @summary Clears all known topics.
*
* @alias module:pubst.clearAll
*/

@@ -289,2 +442,4 @@ function clearAll() {

return {
addTopic: addTopic,
addTopics: addTopics,
clear: clear,

@@ -291,0 +446,0 @@ clearAll: clearAll,

{
"name": "pubst",
"version": "0.2.1",
"version": "0.3.0",
"description": "A slightly opinionated pub/sub event emitter for JavaScript.",
"main": "lib/pubst.js",
"scripts": {
"build": "babel src -d lib",
"clean": "shx rm -rf lib doc",
"build-docs": "jsdoc ./src -d ./doc",
"lint": "eslint ./src",
"prepublish": "babel src -d lib",
"test": "mocha ./src/*.test.js"
"prepare": "npm-run-all clean build-docs build",
"test": "mocha ./src/*.test.js",
"verify": "npm-run-all lint test"
},

@@ -34,10 +38,13 @@ "repository": {

"babel-cli": "6.26.0",
"babel-preset-env": "1.6.1",
"babel-preset-env": "1.7.0",
"chai": "4.1.2",
"clear-require": "2.0.0",
"eslint": "4.19.1",
"mocha": "5.1.1",
"sinon": "4.5.0",
"sinon-chai": "3.0.0"
"eslint": "5.5.0",
"jsdoc": "3.5.5",
"mocha": "5.2.0",
"npm-run-all": "4.1.3",
"shx": "0.3.2",
"sinon": "6.2.0",
"sinon-chai": "3.2.0"
}
}

@@ -22,2 +22,4 @@ # Pubst

While it isn't necessarily required, it is a good idea to configure the topics you are going to use.
```js

@@ -27,8 +29,18 @@ // File1.js

pubst.addTopic({
name: 'NAME',
default: 'World'
});
```
```js
// File2.js
const pubst = require('pubst');
pubst.subscribe('NAME', name => {
console.log(`Hello ${name}!`);
}, 'World');
});
```
Because the subscriber passed a default value of 'World' and the topic is currently empty, the subscriber is primed with the default value.
Because the topic has a default value of 'World' and the topic is currently empty, the subscriber is primed with the default value.
```

@@ -60,3 +72,3 @@ Hello World!

Each subscriber is free to define their own default for each topic.
A subscriber is free to override the default value for their topics.

@@ -72,2 +84,3 @@ ## API

+ `showWarnings` (default: true) - Show warnings in the console.
+ `topics` - An array of topic configurations.

@@ -77,5 +90,59 @@ #### Example

```js
pubst.configure({showWarnings: false});
pubst.configure({
showWarnings: false,
topics: [
{
name: 'user.basicInfo',
default: {
lastName: 'No User Logged In',
firstName: 'No User Logged In'
}
}
]
});
```
### `addTopic(topicConfig)` or `addTopics(topicConfigArrays)`
Sets the configuration for a new topic.
#### 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 to `true` if 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.
#### Examples
```js
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
}
]);
pubst.addTopic({
name: 'player.name',
default: 'Player 1'
});
```
### `publish(topic, payload)`

@@ -206,2 +273,1 @@

2. Experiment with creating a React library that makes linking topics with props mostly painless.
3. Add topic-wide configuration (like: defaults)
/**
* Pubst - Basic JavaScript Pub/Sub Event Emitter
* @module pubst
* Pubst - A slightly opinionated pub/sub library for JavaScript.
*
* Copyright 2017 Jason Schindler
* Copyright 2017-2018 Jason Schindler
*

@@ -18,2 +17,8 @@ * Licensed under the Apache License, Version 2.0 (the "License");

* limitations under the License.
*
* @file pubst.js
* @module pubst
* @author Jason Schindler
* @copyright Jason Schindler 2017-2018
* @description A slightly opinionated pub/sub library for JavaScript.
*/

@@ -48,7 +53,15 @@

/**
* Set global configuration.
* @summary Set global configuration.
*
* @alias module:pubst.configure
* @param {Object} config - Your configuration
*
* @description
* <p>
* Available options are:
* `showWarnings` (default: true) - Show warnings in the console.
* <ul>
* <li>`showWarnings` (default: true) - Show warnings in the console.</li>
* <li>`topics` - An array of topic configurations. (See: `addTopic` for topic configuration options)</li>
* </ul>
* </p>
*/

@@ -61,2 +74,6 @@ function configure(userConfig = {}) {

}
if (Array.isArray(userConfig.topics)) {
addTopics(userConfig.topics);
}
}

@@ -67,3 +84,98 @@

let regexSubs = [];
const topics = {};
const DEFAULT_TOPIC_CONFIG = {
name: '',
default: undefined,
eventOnly: false,
doPrime: true,
allowRepeats: false
};
const ALLOWED_SUB_PROPS = [
'topic',
'handler',
'default',
'doPrime',
'allowRepeats'
];
function buildConfig(base, extensions) {
const result = {};
for (const key in base) {
result[key] = base[key];
}
for (const key in extensions) {
if (result.hasOwnProperty(key)) {
result[key] = extensions[key];
}
}
return result;
}
/**
* @summary Configure a new topic.
*
* @alias module:pubst.addTopic
* @param {Object} newTopicConfig - Topic configuration
*
* @description
* <p>
* Allows you to configure a new topic in pubst.
*
* Available options are:
* <ul>
* <li>`name` (<strong>REQUIRED</strong>) - A string representing the name of the topic.</li>
* <li>
* `default` (default: undefined) - The default value presented to subscribers when the topic is undefined or null.
* This can be overridden by subscribers.
* </li>
* <li>
* `eventOnly` (default: false) - Set this to true if this topic will not have payload data.
* </li>
* <li>
* `doPrime` (default: true) - Should new subscribers automatically receive the last published value on the topic?
* If no valid value is present, new subscribers will be primed with the default value (if configured).
* This can be overridden by subscribers.
* </li>
* <li>
* `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.
* </li>
* </ul>
* </p>
*/
function addTopic(newTopicConfig) {
const topic = buildConfig(DEFAULT_TOPIC_CONFIG, newTopicConfig);
if (!topic.name) {
throw new Error('Topics must have a name.');
}
if (topics[topic.name]) {
warn(`The '${topic.name}' topic has already been configured. The previous configuration will be overwritten.`);
}
topics[topic.name] = topic;
}
/**
* @summary Configure new topics.
*
* @alias module:pubst.addTopics
* @param {Array<Object>} newTopicConfigs - Topic configurations
*
* @description
* <p>
* Allows you to configure new topics. This will call `addTopic` with each item passed.
* For available options, see `addTopic`.
*/
function addTopics(topics) {
topics.forEach(addTopic);
}
function getStringSubsFor(topic) {

@@ -79,4 +191,11 @@ return Array.isArray(stringSubs[topic]) ? stringSubs[topic] : [];

if (typeof subscriber.topic === 'string') {
if (!topics[subscriber.topic]) {
warn(`Adding a subscriber to non-configured topic '${subscriber.topic}'`);
}
stringSubs[subscriber.topic] = getStringSubsFor(subscriber.topic).concat(subscriber);
} else if (subscriber.topic instanceof RegExp) {
const matchCount = Object.keys(topics).filter(topic => topic.match(subscriber.topic)).length;
if (matchCount === 0) {
warn(`Adding a RegExp subscriber that matches no configured topics.`);
}
regexSubs.push(subscriber);

@@ -100,4 +219,12 @@ } else {

function isUndefined(input) {
return typeof input === 'undefined';
}
function isDefined(input) {
return !isUndefined(input);
}
function isNotSet(item) {
return item === null || typeof item === 'undefined';
return item === null || isUndefined(item);
}

@@ -110,3 +237,3 @@

function valueOrDefault(value, def) {
if(isNotSet(value) && typeof def !== 'undefined'){
if(isNotSet(value) && isDefined(def)){
return def;

@@ -118,15 +245,27 @@ }

function getTopicConfig(topic) {
return topics[topic] || buildConfig(DEFAULT_TOPIC_CONFIG, {name: topic});
}
function scheduleCall(sub, payload, topic) {
setTimeout(() => {
const value = valueOrDefault(payload, sub.default);
if (sub.allowRepeats || sub.lastVal !== value || sub.lastTopic !== topic) {
const topicConfig = getTopicConfig(topic);
const defVal = typeof sub.default === 'undefined' ? topicConfig.default : sub.default;
const eventOnly = sub.hasOwnProperty('eventOnly') ? sub.eventOnly : topicConfig.eventOnly;
const allowRepeats = sub.hasOwnProperty('allowRepeats') ? sub.allowRepeats : topicConfig.allowRepeats;
const value = eventOnly ? topic : valueOrDefault(payload, defVal);
if (eventOnly || allowRepeats || sub.lastVal !== value || sub.lastTopic !== topic) {
setTimeout(() => {
sub.handler(value, topic);
sub.lastVal = value;
sub.lastTopic = topic;
}
}, 0);
}, 0);
}
}
/**
* Publish to a topic
* @summary Publish to a topic
*
* @alias module:pubst.publish
* @param {string} topic - The topic to publish to

@@ -136,2 +275,6 @@ * @param {*} payload The payload to publish

function publish(topic, payload) {
if (!topics[topic]) {
warn(`Received a publish for ${topic} but that topic has not been configured.`);
}
store[topic] = payload;

@@ -150,5 +293,8 @@ const subs = allSubsFor(topic);

/**
* Subscribe to one or more topics
* @summary Subscribe to one or more topics
*
* @alias module:pubst.subscribe
* @param {string|RegExp} topic - The topic to receive updates for
* @param {Function|Object} handler
* @param {Function|Object} handler - Either your handler function or
* a subscription configuration object
* @param {*} default - (Optional) Value to send when topic is empty

@@ -159,2 +305,4 @@ *

*
* @description
* <p>
* The first argument may be a string or a regular expression.

@@ -165,36 +313,38 @@ * If a string is provided, the handler will be called for all

* expression.
* </p>
*
* <p>
* The second argument may be a function or an object. The object
* is necessary if you want to provide configuration options for
* this subscription. Available options are:
* <ul>
* <li>`default` - (Default: undefined) - Default value for this sub.</li>
* <li>`doPrime` - (Default: true) - Should the handler be primed with
* the last value?</li>
* <li>`allowRepeats` - (Default: false) - Should the handler be called
* when the value doesn't change?</li>
* <li>`handler` - (Required) - The handler to call.</li>
* </ul>
* </p>
*
* `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
* `handler` - (Required) - The handler to call.
*
* <p>
* 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.
* </p>
*/
function subscribe(topic, handler, def) {
const subscription = {
topic,
default: undefined,
doPrime: true,
allowRepeats: false,
handler: () => {}
};
let subscription;
if (typeof handler === 'function') {
subscription.default = def;
subscription.handler = handler;
subscription = {topic, default: def, handler};
} else if (typeof handler === 'object') {
for (const key in handler) {
if (subscription.hasOwnProperty(key)) {
subscription = {};
Object.keys(handler)
.filter(key => ALLOWED_SUB_PROPS.includes(key))
.forEach(key => {
subscription[key] = handler[key];
}
}
});
subscription.topic = topic;
}

@@ -204,26 +354,27 @@

if (subscription.doPrime) {
let stored;
let stored;
if (typeof topic === 'string') {
stored = [{
topic,
val: currentVal(topic, def)
}];
} else if (topic instanceof RegExp) {
stored = Object.keys(store).filter(key => key.match(topic)).map(key => {
return {
topic: key,
val: currentVal(key, def)
};
});
}
stored.forEach(item => {
if (isSet(item.val)) {
scheduleCall(subscription, item.val, item.topic);
}
if (typeof topic === 'string') {
stored = [{
topic,
val: currentVal(topic, def)
}];
} else if (topic instanceof RegExp) {
stored = Object.keys(store).filter(key => key.match(topic)).map(key => {
return {
topic: key,
val: currentVal(key, def)
};
});
}
stored.forEach(item => {
const topicConfig = getTopicConfig(item.topic);
const doPrime = subscription.hasOwnProperty('doPrime') ? subscription.doPrime : topicConfig.doPrime;
if (doPrime && (topicConfig.eventOnly || isSet(item.val))) {
scheduleCall(subscription, item.val, item.topic);
}
});
return () => {

@@ -235,3 +386,5 @@ removeSub(subscription);

/**
* Get the current value of a topic.
* @summary Get the current value of a topic.
*
* @alias module:pubst.currentVal
* @param {string} topic - The topic to get the value of.

@@ -243,10 +396,13 @@ * @param {*} default - (Optional) a value to return if the topic is

function currentVal(topic, def) {
return valueOrDefault(store[topic], def);
const defToUse = isDefined(def) ? def : getTopicConfig(topic).default;
return valueOrDefault(store[topic], defToUse);
}
/**
* Clears a given topic.
* @summary Clears a given topic.
*
* @alias module:pubst.clear
* @param {string} topic - The topic to clear
*
* Clears the topic by publishing a `null` to it.
* @description Clears the topic by publishing a `null` to it.
*/

@@ -260,3 +416,5 @@ function clear(topic) {

/**
* Clears all known topics.
* @summary Clears all known topics.
*
* @alias module:pubst.clearAll
*/

@@ -268,2 +426,4 @@ function clearAll() {

return {
addTopic,
addTopics,
clear,

@@ -270,0 +430,0 @@ clearAll,

@@ -71,2 +71,26 @@ const clearRequire = require('clear-require');

});
it('returns topic default if value was not set and no default is provided', () => {
const myDefault = 'some default';
pubst.addTopic({
name: TEST_TOPIC_1,
default: myDefault
});
expect(pubst.currentVal(TEST_TOPIC_1)).to.equal(myDefault);
});
it('returns provided default if value was not set and topic was configured with a default', () => {
const myDefault = 'some default';
const topicDefault = 'topic default';
pubst.addTopic({
name: TEST_TOPIC_1,
default: topicDefault
});
expect(pubst.currentVal(TEST_TOPIC_1)).to.equal(topicDefault);
expect(pubst.currentVal(TEST_TOPIC_1, myDefault)).to.equal(myDefault);
});
});

@@ -91,2 +115,423 @@

describe('topic config', () => {
describe('name', () => {
it('is required', () => {
let errorThrown = false;
try {
pubst.addTopic({});
} catch (e) {
errorThrown = true;
}
expect(errorThrown).to.be.true;
});
});
describe('default', () => {
it('is configurable', () => {
const handler = sinon.spy();
const defaultVal = 'DEFAULT VALUE';
pubst.addTopic({
name: TEST_TOPIC_1,
default: defaultVal,
doPrime: false
});
pubst.publish(TEST_TOPIC_1, 'SOME VALUE');
pubst.subscribe(TEST_TOPIC_1, handler);
pubst.clear(TEST_TOPIC_1);
clock.tick(1);
expect(handler).to.have.been.calledWith(defaultVal, TEST_TOPIC_1);
});
it('is off by default', () => {
const handler = sinon.spy();
pubst.addTopic({
name: TEST_TOPIC_1,
doPrime: true
});
pubst.subscribe(TEST_TOPIC_1, handler);
clock.tick(1);
expect(handler).not.to.have.been.called;
});
it('is sent again after a clear', () => {
const handler = sinon.spy();
const defaultVal = 'SOME DEFAULT';
const aValue = 'A real value';
pubst.addTopic({
name: TEST_TOPIC_1,
default: defaultVal,
doPrime: true
});
pubst.subscribe(TEST_TOPIC_1, handler);
clock.tick(1);
expect(handler).to.have.been.calledWith(defaultVal, TEST_TOPIC_1);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1, aValue);
clock.tick(1);
expect(handler).to.have.been.calledWith(aValue, TEST_TOPIC_1);
handler.resetHistory();
pubst.clear(TEST_TOPIC_1);
clock.tick(1);
expect(handler).to.have.been.calledWith(defaultVal, TEST_TOPIC_1);
});
it('can be overriden', () => {
const handler1 = sinon.spy();
const handler2 = sinon.spy();
const handler3 = sinon.spy();
const topicDefault = 'topic default';
const sub2Default = 'sub 2 default';
const sub3Default = 'sub 3 default';
const someValue = 'some value';
pubst.addTopic({
name: TEST_TOPIC_1,
default: topicDefault,
doPrime: false
});
pubst.subscribe(TEST_TOPIC_1, handler1);
pubst.subscribe(TEST_TOPIC_1, handler2, sub2Default);
pubst.subscribe(TEST_TOPIC_1, {
handler: handler3,
default: sub3Default
});
pubst.publish(TEST_TOPIC_1, someValue);
clock.tick(1);
expect(handler1).to.have.been.calledWith(someValue, TEST_TOPIC_1);
expect(handler2).to.have.been.calledWith(someValue, TEST_TOPIC_1);
expect(handler3).to.have.been.calledWith(someValue, TEST_TOPIC_1);
handler1.resetHistory();
handler2.resetHistory();
handler3.resetHistory();
pubst.clear(TEST_TOPIC_1);
clock.tick(1);
expect(handler1).to.have.been.calledWith(topicDefault, TEST_TOPIC_1);
expect(handler2).to.have.been.calledWith(sub2Default, TEST_TOPIC_1);
expect(handler3).to.have.been.calledWith(sub3Default, TEST_TOPIC_1);
});
});
describe('eventOnly', () => {
it('creates topics that do not publish a payload', () => {
const handler = sinon.spy();
pubst.addTopic({
name: TEST_TOPIC_1,
eventOnly: true,
doPrime: true
});
pubst.subscribe(TEST_TOPIC_1, handler);
clock.tick(1);
expect(handler).to.have.been.calledWith(TEST_TOPIC_1);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1, 'some ignored payload');
clock.tick(1);
expect(handler).to.have.been.calledWith(TEST_TOPIC_1);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1);
pubst.publish(TEST_TOPIC_1);
pubst.publish(TEST_TOPIC_1);
clock.tick(1);
expect(handler).to.have.been.calledThrice;
});
it('is off by default', () => {
const payload = 'a payload';
const handler = sinon.spy();
pubst.addTopic({
name: TEST_TOPIC_1,
doPrime: false
});
pubst.subscribe(TEST_TOPIC_1, handler);
pubst.publish(TEST_TOPIC_1, payload);
clock.tick(1);
expect(handler).to.have.been.calledOnce;
expect(handler).to.have.been.calledWith(payload);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1, payload);
clock.tick(1);
expect(handler).not.to.have.been.called;
});
});
describe('doPrime', () => {
it('sends current value to new subscribers when on', () => {
pubst.addTopics([
{
name: TEST_TOPIC_1,
doPrime: true
},
{
name: TEST_TOPIC_2,
doPrime: false
}
]);
const testPayload = 'some test payload';
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_2, testPayload);
const handler1 = sinon.spy();
const handler2 = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, handler1);
pubst.subscribe(TEST_TOPIC_2, handler2);
clock.tick(1);
expect(handler1).to.have.been.calledOnce;
expect(handler1).to.have.been.calledWith(testPayload);
expect(handler2).not.to.have.been.called;
});
it('can be overriden', () => {
pubst.addTopics([
{
name: TEST_TOPIC_1,
doPrime: true
},
{
name: TEST_TOPIC_2,
doPrime: false
}
]);
const testPayload = 'some test payload';
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_2, testPayload);
const handler1 = sinon.spy();
const handler2 = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, {
doPrime: false,
handler: handler1
});
pubst.subscribe(TEST_TOPIC_2, {
doPrime: true,
handler: handler2
});
clock.tick(1);
expect(handler1).not.to.have.been.called;
expect(handler2).to.have.been.calledOnce;
expect(handler2).to.have.been.calledWith(testPayload);
});
it('is on by default', () => {
pubst.addTopics([
{
name: TEST_TOPIC_1
}
]);
const testPayload = 'some test payload';
pubst.publish(TEST_TOPIC_1, testPayload);
const handler = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, handler);
clock.tick(1);
expect(handler).to.have.been.calledOnce;
expect(handler).to.have.been.calledWith(testPayload);
});
it('sends default value if no other value is available', () => {
const defaultPayload = 'some test payload';
pubst.addTopics([
{
name: TEST_TOPIC_1,
doPrime: true,
default: defaultPayload
}
]);
const handler = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, handler);
clock.tick(1);
expect(handler).to.have.been.calledOnce;
expect(handler).to.have.been.calledWith(defaultPayload);
});
});
describe('allowRepeats', () => {
it('calls subs when the value does not change', () => {
pubst.addTopics([
{
name: TEST_TOPIC_1,
allowRepeats: true
},
{
name: TEST_TOPIC_2,
allowRepeats: false
}
]);
const testPayload = 'some test payload';
const handler1 = sinon.spy();
const handler2 = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, handler1);
pubst.subscribe(TEST_TOPIC_2, handler2);
clock.tick(1);
expect(handler1).not.to.have.been.called;
expect(handler2).not.to.have.been.called;
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_2, testPayload);
clock.tick(1);
expect(handler1).to.have.been.calledWith(testPayload, TEST_TOPIC_1);
expect(handler2).to.have.been.calledWith(testPayload, TEST_TOPIC_2);
handler1.resetHistory();
handler2.resetHistory();
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_2, testPayload);
clock.tick(1);
expect(handler1).to.have.been.calledWith(testPayload, TEST_TOPIC_1);
expect(handler2).not.to.have.been.called;
});
it('is off by default', () => {
pubst.addTopic({
name: TEST_TOPIC_1
});
const testPayload = 'some test payload';
const handler = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, handler);
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
expect(handler).to.have.been.calledOnceWith(testPayload, TEST_TOPIC_1);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1, 'something else');
clock.tick(1);
expect(handler).to.have.been.calledOnceWith('something else', TEST_TOPIC_1);
handler.resetHistory();
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
expect(handler).to.have.been.calledOnceWith(testPayload, TEST_TOPIC_1);
});
it('can be overriden', () => {
pubst.addTopic({
name: TEST_TOPIC_1,
allowRepeats: false
});
const testPayload = 'some test payload';
const handler = sinon.spy();
pubst.subscribe(TEST_TOPIC_1, {
allowRepeats: true,
handler
});
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
pubst.publish(TEST_TOPIC_1, testPayload);
pubst.publish(TEST_TOPIC_1, testPayload);
clock.tick(1);
expect(handler).to.have.been.calledThrice;
expect(handler).to.have.been.calledWith(testPayload, TEST_TOPIC_1);
});
});
});
describe('publish & subscribe', () => {

@@ -93,0 +538,0 @@ it('calls the subscriber if the topic already has a set value', () => {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc