dbl-utils
Version:
Utilities for dbl, adba and others projects
171 lines (170 loc) • 6.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventHandler = void 0;
/**
* Class EventHandler to manage event subscriptions and dispatching.
*
* @example
* ```ts
* const handler = new EventHandler();
* handler.subscribe('say', msg => console.log(msg), 'id1');
* handler.dispatch('say', 'hello');
* ```
*/
class EventHandler {
/**
* Construct an instance of EventHandler.
*/
constructor() {
this.events = {};
this.patterns = [];
this.cache = {};
}
/**
* Converts a wildcard string to a regular expression.
* @param wildcardString - The wildcard string to convert.
* @returns A RegExp object.
*/
wildcardToRegExp(wildcardString) {
return new RegExp('^' + wildcardString.split('*').map(this.escapeRegExp).join('.*') + '$');
}
/**
* Escapes special characters in a string for use in a regular expression.
* @param string - The string to escape.
* @return The escaped string.
*/
escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Dispatches an event to all subscribed callbacks.
* @param event - The event name to dispatch.
* @param data - Data to be passed to the callback function.
* @returns A promise resolved with an array of callback responses.
*
* @example
* ```ts
* await handler.dispatch('say', 'hi');
* ```
*/
dispatch(event, ...data) {
return __awaiter(this, void 0, void 0, function* () {
if (this.cache[event]) {
const promises = this.cache[event].map(([callback, id]) => callback(...data, id));
return yield Promise.all(promises);
}
const callbacks = this.events[event] || [];
const matchedPatterns = this.patterns.filter(({ pattern }) => pattern.test(event)).flatMap(p => p.callbacks);
this.cache[event] = [...callbacks, ...matchedPatterns];
const allCallbacks = [...callbacks, ...matchedPatterns];
const promises = allCallbacks.map(([callback, id]) => callback(...data, id));
return yield Promise.all(promises);
});
}
/**
* Subscribes to an event or a pattern of events.
* @param eventString - The event name or pattern to subscribe to.
* @param callback - The callback function to execute when the event is dispatched.
* @param id - An identifier for the subscription, used for unsubscribing.
*
* @example
* ```ts
* handler.subscribe('user.*', cb, 'id');
* ```
*/
subscribe(eventString, callback, id) {
const events = eventString.split(/[\s,]+/);
events.forEach(e => {
if (e.includes('*')) {
// Handle wildcard patterns
const regex = this.wildcardToRegExp(e);
const existingPattern = this.patterns.find(p => p.pattern.source === regex.source);
if (existingPattern) {
existingPattern.callbacks.push([callback, id]);
}
else {
this.patterns.push({ pattern: regex, callbacks: [[callback, id]] });
}
}
else {
// Handle direct event subscriptions
if (!this.events[e])
this.events[e] = [];
this.events[e].push([callback, id]);
if (!this.cache[e])
this.cache[e] = [];
this.cache[e].push([callback, id]);
}
});
}
/**
* Unsubscribes from an event or pattern of events.
* @param eventString - The event name or pattern to unsubscribe from.
* @param id - The identifier of the subscription to remove.
*
* @example
* ```ts
* handler.unsubscribe('user.*', 'id');
* ```
*/
unsubscribe(eventString, id) {
const events = eventString.split(/[\s,]+/);
events.forEach(e => {
if (e.includes('*')) {
const regex = this.wildcardToRegExp(e);
this.patterns = this.patterns.filter(pattern => {
if (pattern.pattern.source !== regex.source)
return true;
pattern.callbacks = pattern.callbacks.filter(([, callbackId]) => callbackId !== id);
return pattern.callbacks.length > 0;
});
this.updateCacheWithPattern(regex, id);
}
else {
if (!this.events[e])
return;
this.events[e] = this.events[e].filter(([, eventId]) => eventId !== id);
this.updateCache(e, id);
}
});
}
/**
* Remove an event or pattern from the cache based on id.
* @param event - The event name or pattern.
* @param id - The identifier of the subscription to remove from the cache.
*/
updateCache(event, id) {
if (this.cache[event]) {
this.cache[event] = this.cache[event].filter(([, eventId]) => eventId !== id);
if (this.cache[event].length === 0) {
delete this.cache[event];
}
}
}
/**
* Update cache by removing pattern-matching events based on id.
* @param regex - The regular expression pattern to match against.
* @param id - The identifier for the subscription to be removed from the cache.
*/
updateCacheWithPattern(regex, id) {
Object.keys(this.cache).forEach(cacheEvent => {
if (regex.test(cacheEvent)) {
this.cache[cacheEvent] = this.cache[cacheEvent].filter(([, callbackId]) => callbackId !== id);
if (this.cache[cacheEvent].length === 0) {
delete this.cache[cacheEvent];
}
}
});
}
}
exports.EventHandler = EventHandler;
exports.default = new EventHandler();