evently-react
Version:
A Hook-Based Framework for Event-Driven React Apps
112 lines (111 loc) • 4.43 kB
JavaScript
import { CacheEnabled, CacheTTL } from '../constants';
import { Logger } from '../utils';
export class EventBus {
constructor(options = {}) {
this.events = new Map();
this.globalMiddlewares = [];
this.eventSpecificMiddlewares = new Map();
this.cache = new Map();
// Apply options or defaults
this.cacheTTL = options.cacheTTL || CacheTTL;
this.cacheEnabled = options.cacheEnabled !== undefined ? options.cacheEnabled : CacheEnabled;
this.logger = options.logger || Logger;
}
// Emit an event
emit(event, payload) {
let processedPayload = payload;
try {
// Apply global middlewares
for (const middleware of this.globalMiddlewares) {
processedPayload = middleware(event, processedPayload);
}
// Apply event-specific middlewares
const eventMiddlewares = this.eventSpecificMiddlewares.get(event) || [];
for (const middleware of eventMiddlewares) {
processedPayload = middleware(event, processedPayload);
}
// Cache the event if enabled
if (this.cacheEnabled) {
this.cache.set(event, { payload: processedPayload, timestamp: Date.now() });
}
const subscribers = this.events.get(event) || [];
// Process subscribers by priority
subscribers
.sort((a, b) => b.priority - a.priority) // Higher priority first
.forEach(subscriber => {
try {
subscriber.callback(processedPayload);
}
catch (callbackError) {
this.logger.error(`Error in callback for event "${event}":`, callbackError);
}
});
}
catch (emitError) {
this.logger.error(`Error emitting event "${event}":`, emitError);
}
}
// Subscribe to an event with priority
subscribe(event, callback, priority = 0) {
if (!event || typeof event !== 'string') {
this.logger.error(`Invalid event name: "${event}"`);
throw new Error(`Invalid event name: "${event}"`);
}
if (typeof callback !== 'function') {
this.logger.error(`Invalid callback for event "${event}". Must be a function.`);
throw new Error(`Invalid callback for event "${event}".`);
}
try {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)?.push({ callback, priority });
// Replay cached event if enabled and valid
if (this.cacheEnabled) {
const cachedEvent = this.cache.get(event);
if (cachedEvent && Date.now() - cachedEvent.timestamp <= this.cacheTTL) {
callback(cachedEvent.payload);
}
}
// Return an unsubscribe function
return () => {
this.unsubscribe(event, callback);
};
}
catch (subscribeError) {
this.logger.error(`Error subscribing to event "${event}":`, subscribeError);
throw subscribeError;
}
}
// Unsubscribe from an event
unsubscribe(event, callback) {
try {
const subscribers = this.events.get(event) || [];
this.events.set(event, subscribers.filter(subscriber => subscriber.callback !== callback));
}
catch (unsubscribeError) {
this.logger.error(`Error unsubscribing from event "${event}":`, unsubscribeError);
}
}
// Register a global middleware
use(middleware) {
try {
this.globalMiddlewares.push(middleware);
}
catch (middlewareError) {
this.logger.error(`Error registering global middleware:`, middlewareError);
}
}
// Register an event-specific middleware
useForEvent(event, middleware) {
try {
if (!this.eventSpecificMiddlewares.has(event)) {
this.eventSpecificMiddlewares.set(event, []);
}
this.eventSpecificMiddlewares.get(event)?.push(middleware);
}
catch (middlewareError) {
this.logger.error(`Error registering middleware for event "${event}":`, middlewareError);
}
}
}