micro-spark
Version:
Micro-Spark is a modern, lightweight, and environment-agnostic event emitter library built in TypeScript. It is designed for complex event-driven systems across various environments such as Node.js, Deno, Bun, and browsers. With features like wildcard eve
179 lines (177 loc) • 5.63 kB
JavaScript
class MicroSpark {
events;
weakListeners;
errorListeners = [];
/**
* Creates a new instance of the Spark event emitter.
*/
constructor() {
this.events = /* @__PURE__ */ new Map();
this.weakListeners = /* @__PURE__ */ new WeakMap();
}
/**
* Registers a listener for a specific event.
*
* @param {keyof Events} event - The name of the event or a wildcard pattern (e.g., `user.*`).
* @param {Listener<Events[Event]>} listener - The function to be called when the event is emitted.
*
* @example
* emitter.on('message', (msg) => {
* console.log(`Message: ${msg}`);
* });
*/
on(event, listener) {
const pattern = this.getWildcardPattern(event) || event;
if (!this.events.has(pattern)) {
this.events.set(pattern, []);
}
this.events.get(pattern)?.push(listener);
if (typeof listener === "object") {
const listenersSet = this.weakListeners.get(listener) || /* @__PURE__ */ new Set();
listenersSet.add(listener);
this.weakListeners.set(listener, listenersSet);
}
}
/**
* Registers a one-time listener for a specific event.
*
* @param event - The name of the event or a wildcard pattern.
* @param listener - The function to be called when the event is emitted.
* @param maxEmits - The maximum number of times the listener should be invoked. Defaults to 1 if not provided.
*
* @example
* emitter.once('message', (msg) => {
* console.log(`One-time message: ${msg}`);
* });
*/
once(event, listener, maxEmits = 1) {
let emitCount = 0;
const onceWrapper = (...args) => {
if (emitCount < maxEmits) {
listener(...args);
emitCount++;
}
if (emitCount >= maxEmits) {
this.off(event, onceWrapper);
}
};
this.on(event, onceWrapper);
}
/**
* Removes a listener from a specific event.
*
* @param {keyof Events} event - The name of the event or a wildcard pattern.
* @param {Listener<Events[Event]>} [listener] - The listener function to be removed. If not provided, all listeners for the event will be removed.
*
* @example
* emitter.off('message', listener);
*/
off(event, listener) {
const pattern = this.getWildcardPattern(event) || event;
if (this.events.has(pattern)) {
if (!listener) {
this.events.delete(pattern);
} else {
const listeners = this.events.get(pattern);
if (!listeners) return;
const filtered = listeners.filter((l) => l !== listener);
this.events.set(pattern, filtered);
}
}
}
/**
* Emits an event, invoking all matching listeners.
*
* @param {keyof Events} event - The name of the event.
* @param {...Events[Event]} args - Arguments to be passed to the listeners.
*
* @returns {EmitResult | Promise<EmitResult>} The result of the emit operation, including any errors.
*
* @example
* emitter.emit('message', 'Hello, World!');
*/
emit(event, ...args) {
const errors = [];
const promises = [];
const processedArgs = args.map(
(arg) => typeof arg === "function" ? arg() : arg
);
for (const [key, listeners] of this.events.entries()) {
if (typeof key === "string" && this.eventMatches(key, event)) {
for (const listener of listeners) {
try {
if (key.includes("*")) {
const result = listener(event, ...processedArgs);
if (result instanceof Promise) promises.push(result);
} else {
const result = listener(...processedArgs);
if (result instanceof Promise) promises.push(result);
}
} catch (err) {
this.emitError(event, err);
errors.push(err);
}
}
}
}
if (promises.length > 0) {
return Promise.all(promises).then(
() => errors.length > 0 ? { success: false, errors } : { success: true }
).catch((err) => {
this.emitError(event, err);
errors.push(err);
return { success: false, errors };
});
}
return errors.length > 0 ? { success: false, errors } : { success: true };
}
/**
* Registers an error handler for the event emitter.
*
* @param {ErrorListener} handler - A function to handle errors emitted during listener execution.
*
* @example
* emitter.onError((event, error) => {
* console.error(`Error in event "${event}": ${error.message}`);
* });
*/
onError(handler) {
this.errorListeners.push(handler);
}
/**
* Clears the listeners for a specific event, or all listeners if no event is provided.
*
* @param {keyof Events} [event] - The name of the event to clear listeners for. If not provided, clears all listeners.
*
* @example
* emitter.clear('message'); // Clears listeners for the "message" event
* emitter.clear(); // Clears all listeners
*/
clear(event) {
if (event) {
const pattern = this.getWildcardPattern(event) || event;
if (this.events.has(pattern)) {
this.events.delete(pattern);
}
} else {
this.events.clear();
}
}
emitError(event, err) {
for (const listener of this.errorListeners) {
listener(event, err);
}
}
getWildcardPattern(event) {
return event.includes("*") ? event.replace(/\*/g, ".*") : void 0;
}
eventMatches(pattern, event) {
if (pattern.includes(".*")) {
const regex = new RegExp(`^${pattern}$`);
return regex.test(event);
}
return pattern === event;
}
}
export { MicroSpark };
//# sourceMappingURL=micro-spark.es.js.map