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

publisher-subscriber-pattern

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

publisher-subscriber-pattern - npm Package Compare versions

Comparing version 1.3.1 to 2.0.0

dist/_types.d.ts

1

.eslintrc.js

@@ -34,2 +34,3 @@ module.exports = {

'key-spacing': 'error',
'max-len': ['error', { "code": 100 }],
'no-duplicate-imports': 'error',

@@ -36,0 +37,0 @@ 'no-multi-spaces': 'error',

@@ -0,1 +1,10 @@

# @2.0.0 [author: Katarzyna Ziomek-Zdanowicz, date: 2019.09.08]
* changes Publisher parameters: from emitterInstance's methods to names of emitterInstance's methods (functions to strings)
* publisher subscribes to eventEmitter's event only once (bugFix)
* corrects rest operator in Publisher's constructor (bugFix)
* corrects getEventData method, so it returns either eventData or undefined (bugFix)
* adds type : EmitterInstance and type guard: isValidEmitter
* refactors code, among others: separates dir for mocks, separates file for types, removes eventEmitterStub's subscription to its events
* modifies README
# @1.3.1 [author: Katarzyna Ziomek-Zdanowicz, date: 2019.09.07]

@@ -2,0 +11,0 @@ * modifies README

4

dist/index.d.ts

@@ -1,2 +0,2 @@

export { Publisher, PublisherProps } from './publisher';
export { SubscriberEventCallback } from './subscriber';
export { Publisher } from './publisher';
export { EventCallback, PublisherProps, } from './_types';

@@ -12,12 +12,12 @@ "use strict";

});
Object.defineProperty(exports, "PublisherProps", {
Object.defineProperty(exports, "EventCallback", {
enumerable: true,
get: function get() {
return _publisher.PublisherProps;
return _types.EventCallback;
}
});
Object.defineProperty(exports, "SubscriberEventCallback", {
Object.defineProperty(exports, "PublisherProps", {
enumerable: true,
get: function get() {
return _subscriber.SubscriberEventCallback;
return _types.PublisherProps;
}

@@ -28,2 +28,2 @@ });

var _subscriber = require("./subscriber");
var _types = require("./_types");

@@ -1,4 +0,2 @@

import { SubscriberEventCallback } from './subscriber';
export declare type SubscriptionFunctions = (eventName: string, eventCallback: SubscriberEventCallback) => void;
export declare type PublisherProps = [unknown, SubscriptionFunctions, SubscriptionFunctions];
import { EventCallback, PublisherProps } from './_types';
export declare class Publisher {

@@ -8,4 +6,4 @@ private eventData;

private removeEventListener;
constructor(...[emitterInstance, addEventListener, removeEventListener]: PublisherProps);
subscribe: (eventName: string, eventCallback: SubscriberEventCallback, subscriberInstance?: Record<string, unknown> | undefined) => () => void;
constructor(...args: PublisherProps);
subscribe: (eventName: string, eventCallback: EventCallback, subscriberInstance?: Record<string, unknown> | undefined) => () => void;
unsubscribeAll: () => void;

@@ -12,0 +10,0 @@ eventSubscribersCount: (eventName: string) => number;

@@ -8,2 +8,4 @@ "use strict";

var _types = require("./_types");
var _subscriber = require("./subscriber");

@@ -25,10 +27,2 @@

for (var _len = arguments.length, _ref = new Array(_len), _key = 0; _key < _len; _key++) {
_ref[_key] = arguments[_key];
}
var emitterInstance = _ref[0],
addEventListener = _ref[1],
removeEventListener = _ref[2];
_classCallCheck(this, Publisher);

@@ -43,9 +37,13 @@

_defineProperty(this, "subscribe", function (eventName, eventCallback, subscriberInstance) {
var subscriber = new _subscriber.Subscriber(eventCallback, subscriberInstance);
var eventData = _this.getEventData(eventName);
var subscriber = new _subscriber.Subscriber(eventCallback, subscriberInstance);
eventData[1].push(subscriber);
if (!eventData) {
eventData = _this.eventData.set(eventName, [_this.buildInformSubscribers(eventName), []]).get(eventName);
_this.observeEvent(eventName);
_this.observeEvent(eventName, eventData);
}
eventData[1].push(subscriber);
return _this.unsubscribe(eventName, subscriber);

@@ -61,3 +59,3 @@ });

_defineProperty(this, "eventSubscribersCount", function (eventName) {
return _this.getSubscribers(eventName).length;
return (_this.getSubscribers(eventName) || []).length;
});

@@ -70,3 +68,2 @@

count += eventData[1].length;
console.log('count', count);
return count;

@@ -78,3 +75,3 @@ }, 0);

return function (event) {
_this.getSubscribers(eventName).forEach(function (subscriber) {
(_this.getSubscribers(eventName) || []).forEach(function (subscriber) {
return subscriber.eventCallback(event);

@@ -87,3 +84,3 @@ });

return function () {
var subscribersArray = _this.getEventData(eventName)[1];
var subscribersArray = (_this.getEventData(eventName) || [])[1];

@@ -104,4 +101,8 @@ if (subscribersArray) {

_defineProperty(this, "observeEvent", function (eventName) {
_this.addEventListener(eventName, _this.getEventCallback(eventName));
_defineProperty(this, "observeEvent", function (eventName, eventData) {
var eventCallback = eventData ? eventData[0] : _this.getEventCallback(eventName);
if (eventCallback) {
_this.addEventListener(eventName, eventCallback);
}
});

@@ -111,7 +112,21 @@

_this.removeEventListener(eventName, _this.getEventCallback(eventName));
_this.eventData["delete"](eventName);
});
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var emitterInstance = args[0],
addListenerMethodName = args[1],
removeListenerMethodName = args[2];
if (!(0, _types.isValidEmitter)(emitterInstance, addListenerMethodName, removeListenerMethodName)) {
throw new Error('Publisher received incorrect arguments');
}
this.eventData = new Map([]);
this.addEventListener = addEventListener.bind(emitterInstance);
this.removeEventListener = removeEventListener.bind(emitterInstance);
this.addEventListener = emitterInstance[addListenerMethodName].bind(emitterInstance);
this.removeEventListener = emitterInstance[removeListenerMethodName].bind(emitterInstance);
}

@@ -122,3 +137,3 @@

value: function getEventData(eventName) {
return this.eventData.get(eventName) || this.eventData.set(eventName, [this.buildInformSubscribers(eventName), []]).get(eventName);
return this.eventData.get(eventName);
}

@@ -128,3 +143,3 @@ }, {

value: function getEventCallback(eventName) {
return this.getEventData(eventName)[0];
return (this.getEventData(eventName) || [])[0];
}

@@ -134,3 +149,3 @@ }, {

value: function getSubscribers(eventName) {
return this.getEventData(eventName)[1];
return (this.getEventData(eventName) || [])[1];
}

@@ -137,0 +152,0 @@ }]);

@@ -1,5 +0,7 @@

export declare type SubscriberEventCallback = (event: Event) => void;
import { EventCallback } from './_types';
declare type SubscriberInstance = Record<string, unknown>;
export declare class Subscriber {
eventCallback: SubscriberEventCallback;
constructor(eventCallback: SubscriberEventCallback, subscriberInstance?: Record<string, unknown>);
eventCallback: EventCallback;
constructor(eventCallback: EventCallback, subscriberInstance?: SubscriberInstance);
}
export {};
{
"name": "publisher-subscriber-pattern",
"version": "1.3.1",
"version": "2.0.0",
"description": "Publisher subscriber pattern that can be used with different event emitters including browser window",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

# About
* Javascript implementation of publisher subscriber pattern
* Can be used with various event emitters including browser's window object
* Can be used with various event emitters including browser's *window* object
* Provides **Publisher** class

@@ -19,6 +19,6 @@

const publisher = new Publisher(window, window.addEventListener, window.removeEventListener);
const publisher = new Publisher(window, 'addEventListener','removeEventListener');
```
*Publisher's* *subscribe* method returns function, which can be used later to unsubscribe from the event.
*Publisher's* *subscribe* method returns function, which can be used later to unsubscribe from the publisher's event.

@@ -33,3 +33,3 @@ ```javascript

There is also a method *unsubscribeAll* for unsubscribing all subscribers from all events.
There is also a method *unsubscribeAll* for unsubscribing all subscribers from all emitter's events.

@@ -42,3 +42,3 @@ ```javascript

## **Publisher**
* Parameters: *emitterInstance*, *addListener*, *removeListener*
* Parameters: *emitterInstance*, *addListenerMethodName*, *removeListenerMethodName*
* Methods: *subscribe*, *unsubscribeAll*, *eventSubscribersCount*, *subscribersCount*

@@ -49,6 +49,7 @@

#### emitterInstance
* Object that will be bound to *addListener* and *removeListener* as *this* ([see Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind))
* Object, which exposes *addListener* and *removeListener* methods
* Behind the scenes, the *emitterInstance* is bound to *addListener* and *removeListener* methods as *this* ([see Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind))
#### addListener, removeListener
* Methods exposed by *emitterInstance* which *add* and *remove* event listeners, respectively
#### addListenerMethodName, removeListenerMethodName
* Names of methods exposed by *emitterInstance* which add and remove event listeners, respectively

@@ -59,16 +60,16 @@ ### Methods

* Required parameters: *eventName*, *eventCallback*
* Optional parameter: *subscriberInstance* that will be bound to *eventCallback* as *this*
* Subscribes *eventCallback* to *eventName*, so whenever event occurs the *eventCallback* is called (with *subscriberInstance* as *this*, if provided)
* Returns unsubscribe function
* Optional parameter: *subscriberInstance*, which will be bound to the *eventCallback* argument as *this* ([see Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind))
* Method subscribes *eventCallback* to *eventName*, so whenever event occurs the *eventCallback* is called
* Returns function for unsubscribing from the event
#### unsubscribeAll
* Unsubscribes all *eventCallbacks* from all *eventNames*
* Runs *removeListener* of *emitterInstance* fro all *eventNames*
* Unsubscribes all publisher's *eventCallbacks* from all emitter's *eventNames*
#### eventSubscriberCount
* Required parameter: *eventName*
* Returns number of *eventCallbacks* subscribed to the *evenName*
* Returns number of subscribers' *eventCallbacks* subscribed to publisher's *eventName*
#### subscribersCount
* Returns number of *eventCallbacks* subscribed to all *evenNames*
* Returns number of subscriber's *eventCallbacks* subscribed to all publisher's *eventNames*

@@ -75,0 +76,0 @@ # Dependencies

@@ -6,3 +6,4 @@ require('chai');

const sinon = require('sinon');
const eventEmitterStub = require('./event-emitter-stub');
const eventEmitterStub = require('./mocks/event-emitter-stub');
const SubscriberStub = require('./mocks/subscriber-stub');

@@ -13,12 +14,23 @@ describe('Publisher', () => {

const Subscriber = function () {
this.calls = 0;
this.callback = () => this.calls++;
};
const addListenerMethodName = 'addListener';
const removeListenerMethodName = 'removeListener';
global.eventCallback = () => {};
const buildPublisherSubscribers = () => {
const publisher =
new Publisher(eventEmitterStub, addListenerMethodName, removeListenerMethodName);
const firstSubscriber = new SubscriberStub();
const secondSubscriber = new SubscriberStub();
return {
publisher,
firstSubscriber,
secondSubscriber,
};
};
beforeEach(function () {
sinon.spy( eventEmitterStub, 'addListener');
sinon.spy( eventEmitterStub, 'removeListener');
sinon.spy( eventEmitterStub, addListenerMethodName);
sinon.spy( eventEmitterStub, removeListenerMethodName);
sinon.spy( global, 'eventCallback' );

@@ -28,21 +40,28 @@ });

afterEach(function () {
eventEmitterStub.removeAllListeners();
eventEmitterStub.addListener.restore();
eventEmitterStub.removeListener.restore();
eventEmitterStub.removeAllListeners('fooEvent');
eventEmitterStub.removeAllListeners('barEvent');
eventEmitterStub[ addListenerMethodName ].restore();
eventEmitterStub[ removeListenerMethodName ].restore();
global.eventCallback.restore();
});
it('subscribes to event', function () {
it('subscribes to event only once', function () {
// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const publisher =
new Publisher(eventEmitterStub, addListenerMethodName, removeListenerMethodName);
// when
publisher.subscribe(fooEventName, eventCallback);
publisher.subscribe(fooEventName, eventCallback);
// then
const { args: [ eventName, callback ] } = eventEmitterStub.addListener.getCalls()[ 0 ];
const { args: [ passedEventName, passedCallback ] } =
eventEmitterStub[ addListenerMethodName ].getCalls()[ 0 ];
expect(eventEmitterStub.addListener).toBeCalledOnce;
expect(eventName).toBe(fooEventName);
expect(typeof callback).toBe('function');
expect(eventEmitterStub[ addListenerMethodName ]).toBeCalledOnce;
expect(eventEmitterStub.listenerCount(fooEventName)).toBe(1);
expect(passedEventName).toBe(fooEventName);
expect(typeof passedCallback).toBe('function');
});

@@ -52,3 +71,4 @@

// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const publisher =
new Publisher(eventEmitterStub, addListenerMethodName, removeListenerMethodName);

@@ -60,5 +80,6 @@ // when

// then
const { args: [ eventName, callback ] } = eventEmitterStub.removeListener.getCalls()[ 0 ];
const { args: [ eventName, callback ] } =
eventEmitterStub[ removeListenerMethodName ].getCalls()[ 0 ];
expect(eventEmitterStub.removeListener).toBeCalledOnce;
expect(eventEmitterStub[ removeListenerMethodName ]).toBeCalledOnce;
expect(eventName).toBe(barEventName);

@@ -70,3 +91,4 @@ expect(typeof callback).toBe('function');

// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const publisher =
new Publisher(eventEmitterStub, addListenerMethodName, removeListenerMethodName);

@@ -80,13 +102,17 @@ // when

// then
const { args: [ eventName0, callback0 ] } = eventEmitterStub.removeListener.getCalls()[ 0 ];
const { args: [ eventName1, callback1 ] } = eventEmitterStub.removeListener.getCalls()[ 1 ];
expect(eventEmitterStub.removeListener).toBeCalledTwice;
expect(eventName0).toBe(barEventName);
expect(typeof callback0).toBe('function');
expect(eventName1).toBe(fooEventName);
expect(typeof callback1).toBe('function');
const { args: [ firstPassedEventName, firstPassedCallback ] } =
eventEmitterStub[ removeListenerMethodName ].getCalls()[ 0 ];
const { args: [ secondPassedEventName, secondCallback ] } =
eventEmitterStub[ removeListenerMethodName ].getCalls()[ 1 ];
// TODO check number of subscribers
// TODO przed publikacja: zmienic poprzedniego taga, uzupelnic README o przyklad instalacji, wyeksporotwac potrzebne mi typy
// złapać testami to czego nie łapałam poprzednio
expect(eventEmitterStub[ removeListenerMethodName ]).toBeCalledTwice;
expect(firstPassedEventName).toBe(barEventName);
expect(typeof firstPassedCallback).toBe('function');
expect(secondPassedEventName).toBe(fooEventName);
expect(typeof secondCallback).toBe('function');
expect(eventEmitterStub.listenerCount(barEventName)).toBe(0);
expect(eventEmitterStub.listenerCount(fooEventName)).toBe(0);
});

@@ -96,5 +122,3 @@

// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const firstSubscriber = new Subscriber();
const secondSubscriber = new Subscriber();
const { publisher, firstSubscriber, secondSubscriber } = buildPublisherSubscribers();

@@ -104,2 +128,3 @@ // when

publisher.subscribe(barEventName, secondSubscriber.callback, secondSubscriber);
eventEmitterStub.emit(fooEventName);

@@ -110,11 +135,9 @@ eventEmitterStub.emit(barEventName);

// then
expect(firstSubscriber.calls).toBe(2);
expect(secondSubscriber.calls).toBe(1);
expect(firstSubscriber.callsCount).toBe(2);
expect(secondSubscriber.callsCount).toBe(1);
});
it('does not inform subscribers about event', () => {
it('does not inform unsubscribed subscribers about event', () => {
// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const firstSubscriber = new Subscriber();
const secondSubscriber = new Subscriber();
const { publisher, firstSubscriber, secondSubscriber } = buildPublisherSubscribers();

@@ -124,10 +147,11 @@ // when

publisher.subscribe(barEventName, secondSubscriber.callback, secondSubscriber);
publisher.unsubscribeAll();
eventEmitterStub.emit(fooEventName);
eventEmitterStub.emit(barEventName);
eventEmitterStub.emit(fooEventName);
// then
expect(firstSubscriber.calls).toBe(0);
expect(secondSubscriber.calls).toBe(0);
expect(firstSubscriber.callsCount).toBe(0);
expect(secondSubscriber.callsCount).toBe(0);
});

@@ -137,5 +161,3 @@

// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const firstSubscriber = new Subscriber();
const secondSubscriber = new Subscriber();
const { publisher, firstSubscriber, secondSubscriber } = buildPublisherSubscribers();

@@ -152,5 +174,3 @@ // when

// given
const publisher = new Publisher(eventEmitterStub, eventEmitterStub.addListener, eventEmitterStub.removeListener);
const firstSubscriber = new Subscriber();
const secondSubscriber = new Subscriber();
const { publisher, firstSubscriber, secondSubscriber } = buildPublisherSubscribers();

@@ -166,2 +186,8 @@ // when

});
it('throws Error if receives invalid eventEmitter', () => {
// then
expect(() => new Publisher(eventEmitterStub, 'someFooMethod', 'someBarMethod'))
.toThrowError('Publisher received incorrect arguments');
});
});

@@ -1,2 +0,5 @@

export { Publisher, PublisherProps } from './publisher';
export { SubscriberEventCallback } from './subscriber';
export { Publisher } from './publisher';
export {
EventCallback,
PublisherProps,
} from './_types';
import {
Subscriber,
SubscriberEventCallback,
} from './subscriber';
EmitterInstance,
EventCallback,
EventData,
EventName,
PublisherProps,
SubscriptionFunctions,
isValidEmitter
} from './_types';
import { Subscriber } from './subscriber';
type EventName = string;
type EventData = [SubscriberEventCallback, Subscriber[]];
export type SubscriptionFunctions = (eventName: string, eventCallback: SubscriberEventCallback) => void;
export type PublisherProps = [ unknown, SubscriptionFunctions, SubscriptionFunctions ];
export class Publisher {

@@ -16,16 +16,35 @@ private eventData: Map< EventName, EventData>;

private removeEventListener: SubscriptionFunctions;
constructor (...[ emitterInstance, addEventListener, removeEventListener ]: PublisherProps ) {
constructor (...args: PublisherProps) {
const [ emitterInstance, addListenerMethodName, removeListenerMethodName ] = args;
if (!isValidEmitter(emitterInstance, addListenerMethodName, removeListenerMethodName)) {
throw new Error('Publisher received incorrect arguments');
}
this.eventData = new Map([]);
this.addEventListener = addEventListener.bind(emitterInstance);
this.removeEventListener = removeEventListener.bind(emitterInstance);
this.addEventListener =
( emitterInstance[ addListenerMethodName ] as SubscriptionFunctions).bind(emitterInstance);
this.removeEventListener =
( emitterInstance[ removeListenerMethodName ] as SubscriptionFunctions).bind(emitterInstance);
}
public subscribe = (eventName: EventName, eventCallback: SubscriberEventCallback, subscriberInstance?: Record<string, unknown>): () => void => {
const eventData = this.getEventData(eventName);
public subscribe = (
eventName: EventName,
eventCallback: EventCallback,
subscriberInstance?: EmitterInstance
): () => void => {
const subscriber = new Subscriber(eventCallback, subscriberInstance);
let eventData = this.getEventData(eventName);
if (!eventData) {
eventData = this.eventData
.set( eventName, [ this.buildInformSubscribers(eventName), [] ])
.get(eventName) as EventData;
this.observeEvent(eventName, eventData);
}
eventData[ 1 ].push(subscriber);
this.observeEvent(eventName);
return this.unsubscribe(eventName, subscriber);

@@ -41,3 +60,3 @@ }

public eventSubscribersCount = (eventName: EventName): number => {
return this.getSubscribers(eventName).length;
return (this.getSubscribers(eventName) || []).length;
}

@@ -48,3 +67,2 @@

count += eventData[ 1 ].length;
console.log('count', count);
return count;

@@ -54,21 +72,18 @@ }, 0);

private getEventData (eventName: EventName): EventData {
return (
this.eventData.get(eventName) ||
this.eventData.set(eventName, [ this.buildInformSubscribers(eventName), [] ]).get(eventName)
) as EventData;
private getEventData (eventName: EventName): EventData | undefined {
return this.eventData.get(eventName);
}
private getEventCallback (eventName: EventName): SubscriberEventCallback {
return this.getEventData(eventName)[ 0 ];
private getEventCallback (eventName: EventName): EventCallback | undefined {
return (this.getEventData(eventName) || [])[ 0 ];
}
private getSubscribers (eventName: EventName): Subscriber[] {
return this.getEventData(eventName)[ 1 ];
private getSubscribers (eventName: EventName): Subscriber[] | undefined {
return (this.getEventData(eventName) || [])[ 1 ];
}
private buildInformSubscribers = (eventName: EventName): SubscriberEventCallback => (
private buildInformSubscribers = (eventName: EventName): EventCallback => (
(event: Event): void => {
this.getSubscribers(eventName)
.forEach(subscriber => subscriber.eventCallback(event));
(this.getSubscribers(eventName) || [])
.forEach(subscriber => subscriber.eventCallback(event));
}

@@ -79,3 +94,3 @@ )

return (): void => {
const subscribersArray = this.getEventData(eventName)[ 1 ];
const subscribersArray = (this.getEventData(eventName) || [])[ 1 ];

@@ -94,9 +109,17 @@ if (subscribersArray) {

private observeEvent = (eventName: EventName): void => {
this.addEventListener(eventName, this.getEventCallback(eventName));
private observeEvent = (eventName: EventName, eventData?: EventData): void => {
const eventCallback = eventData
? eventData[ 0 ]
: this.getEventCallback(eventName);
if (eventCallback) {
this.addEventListener(eventName, eventCallback);
}
}
private unobserveEvent = (eventName: EventName): void => {
this.removeEventListener(eventName, this.getEventCallback(eventName));
this.removeEventListener(eventName, this.getEventCallback(eventName) as EventCallback);
this.eventData.delete(eventName);
}
}

@@ -1,8 +0,10 @@

export type SubscriberEventCallback = (event: Event) => void;
import { EventCallback } from './_types';
type SubscriberInstance = Record<string, unknown>;
export class Subscriber {
public eventCallback: SubscriberEventCallback;
constructor (eventCallback: SubscriberEventCallback, subscriberInstance?: Record<string, unknown>) {
public eventCallback: EventCallback;
constructor (eventCallback: EventCallback, subscriberInstance?: SubscriberInstance) {
this.eventCallback = eventCallback.bind(subscriberInstance);
}
}
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