@xyo-network/module-events
Advanced tools
Comparing version 2.53.3 to 2.53.4
@@ -7,32 +7,15 @@ "use strict"; | ||
const forget_1 = require("@xylabs/forget"); | ||
const core_1 = require("@xyo-network/core"); | ||
const resolvedPromise = Promise.resolve(); | ||
let canEmitMetaEvents = false; | ||
let isGlobalDebugEnabled = false; | ||
function assertEventName(eventName) { | ||
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') { | ||
throw new TypeError('`eventName` must be a string, symbol, or number'); | ||
} | ||
} | ||
function assertListener(listener) { | ||
if (typeof listener !== 'function') { | ||
throw new TypeError('listener must be a function'); | ||
} | ||
} | ||
const isMetaEvent = (eventName) => eventName === 'listenerAdded' || eventName === 'listenerRemoved'; | ||
class Events { | ||
constructor(options = {}) { | ||
class Events extends core_1.Base { | ||
constructor(params = {}) { | ||
var _a; | ||
//this is here to be able to query the type, not use | ||
this.eventData = {}; | ||
Events.anyMap.set(this, new Set()); | ||
Events.eventsMap.set(this, new Map()); | ||
this.debug = options.debug; | ||
if (this.debug) { | ||
this.debug.enabled = !!this.debug.enabled; | ||
this.debug.logger = | ||
(_a = this.debug.logger) !== null && _a !== void 0 ? _a : ((type, debugName, eventName, eventData) => { | ||
const mutatedParams = Object.assign({}, params); | ||
if (mutatedParams.debug) { | ||
mutatedParams.debug.logger = | ||
(_a = mutatedParams.debug.logger) !== null && _a !== void 0 ? _a : ((type, debugName, eventName, eventData) => { | ||
let eventDataString; | ||
let eventNameString; | ||
try { | ||
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code. | ||
eventDataString = JSON.stringify(eventData); | ||
@@ -51,5 +34,10 @@ } | ||
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}`; | ||
console.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`); | ||
this.logger.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`); | ||
}); | ||
} | ||
super(mutatedParams); | ||
//this is here to be able to query the type, not use | ||
this.eventData = {}; | ||
Events.anyMap.set(this, new Set()); | ||
Events.eventsMap.set(this, new Map()); | ||
} | ||
@@ -61,10 +49,13 @@ static get isDebugEnabled() { | ||
if (typeof ((_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.env) !== 'object') { | ||
return isGlobalDebugEnabled; | ||
return Events.isGlobalDebugEnabled; | ||
} | ||
const { env } = (_b = globalThis.process) !== null && _b !== void 0 ? _b : { env: {} }; | ||
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled; | ||
return env.DEBUG === 'events' || env.DEBUG === '*' || Events.isGlobalDebugEnabled; | ||
} | ||
static set isDebugEnabled(newValue) { | ||
isGlobalDebugEnabled = newValue; | ||
Events.isGlobalDebugEnabled = newValue; | ||
} | ||
get debug() { | ||
return this.params.debug; | ||
} | ||
clearListeners(eventNames) { | ||
@@ -99,7 +90,7 @@ var _a, _b; | ||
try { | ||
canEmitMetaEvents = true; | ||
Events.canEmitMetaEvents = true; | ||
yield this.emitMetaEventInternal(eventName, eventArgs); | ||
} | ||
finally { | ||
canEmitMetaEvents = false; | ||
Events.canEmitMetaEvents = false; | ||
} | ||
@@ -112,31 +103,34 @@ } | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
} | ||
const filterMatch = (args, filter) => { | ||
if (filter) { | ||
switch (typeof filter) { | ||
case 'object': | ||
return Object.entries(args).reduce((prev, [key, value]) => (filter[key] === value ? true : prev), false); | ||
default: | ||
return args === filter; | ||
} | ||
} | ||
return true; | ||
}; | ||
this.logIfDebugEnabled('emitSerial', eventName, eventArgs); | ||
const listeners = (_a = this.getListeners(eventName)) !== null && _a !== void 0 ? _a : new Set(); | ||
const filteredListeners = [...listeners.values()] | ||
.filter((value) => (value.filter ? filterMatch(eventArgs, value.filter) : true)) | ||
.map((info) => info.listener); | ||
const anyListeners = (0, assert_1.assertEx)(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = [...anyListeners]; | ||
yield resolvedPromise; | ||
for (const listener of staticListeners) { | ||
if (listeners.has(listener)) { | ||
yield listener(eventArgs); | ||
} | ||
yield this.safeCallListener(eventName, eventArgs, listener); | ||
} | ||
for (const listener of staticAnyListeners) { | ||
if (anyListeners.has(listener)) { | ||
yield listener(eventName, eventArgs); | ||
} | ||
yield this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
}); | ||
} | ||
getListeners(eventName) { | ||
const events = (0, assert_1.assertEx)(Events.eventsMap.get(this)); | ||
if (!events.has(eventName)) { | ||
return; | ||
} | ||
return events.get(eventName); | ||
} | ||
//TODO: Make test for this | ||
listenerCount(eventNames) { | ||
@@ -151,5 +145,2 @@ var _a, _b; | ||
} | ||
if (typeof eventName !== 'undefined') { | ||
assertEventName(eventName); | ||
} | ||
count += (0, assert_1.assertEx)(Events.anyMap.get(this)).size; | ||
@@ -169,6 +160,4 @@ for (const value of (0, assert_1.assertEx)(Events.eventsMap.get(this)).values()) { | ||
off(eventNames, listener) { | ||
assertListener(listener); | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName); | ||
const set = this.getListeners(eventName); | ||
@@ -189,3 +178,2 @@ if (set) { | ||
offAny(listener) { | ||
assertListener(listener); | ||
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined); | ||
@@ -196,7 +184,5 @@ const typedMap = Events.anyMap.get(this); | ||
} | ||
on(eventNames, listener) { | ||
assertListener(listener); | ||
on(eventNames, listener, filter) { | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName); | ||
let set = this.getListeners(eventName); | ||
@@ -208,3 +194,3 @@ if (!set) { | ||
} | ||
set.add(listener); | ||
set.add({ filter, listener: listener }); | ||
this.logIfDebugEnabled('subscribe', eventName, undefined); | ||
@@ -219,3 +205,2 @@ if (!isMetaEvent(eventName)) { | ||
var _a; | ||
assertListener(listener); | ||
this.logIfDebugEnabled('subscribeAny', undefined, undefined); | ||
@@ -229,3 +214,3 @@ (_a = Events.anyMap.get(this)) === null || _a === void 0 ? void 0 : _a.add(listener); | ||
this.off(eventName, subListener); | ||
yield listener(args); | ||
yield this.safeCallListener(eventName, args, listener); | ||
}); | ||
@@ -235,7 +220,6 @@ this.on(eventName, subListener); | ||
} | ||
emitInternal(eventName, eventArgs) { | ||
emitInternal(eventName, eventArgs, filter) { | ||
var _a; | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
@@ -245,4 +229,5 @@ } | ||
const listeners = (_a = this.getListeners(eventName)) !== null && _a !== void 0 ? _a : new Set(); | ||
const filteredListeners = [...listeners.values()].filter((value) => (filter ? value.listener : true)).map((info) => info.listener); | ||
const anyListeners = (0, assert_1.assertEx)(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners]; | ||
@@ -252,9 +237,7 @@ yield resolvedPromise; | ||
...staticListeners.map((listener) => tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (listeners.has(listener)) { | ||
return yield listener(eventArgs); | ||
} | ||
yield this.safeCallListener(eventName, eventArgs, listener); | ||
})), | ||
...staticAnyListeners.map((listener) => tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (anyListeners.has(listener)) { | ||
return yield listener(eventName, eventArgs); | ||
yield this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
@@ -268,4 +251,3 @@ })), | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
@@ -275,4 +257,5 @@ } | ||
const listeners = (_a = this.getListeners(eventName)) !== null && _a !== void 0 ? _a : new Set(); | ||
const filteredListeners = [...listeners.values()].map((info) => info.listener); | ||
const anyListeners = (0, assert_1.assertEx)(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners]; | ||
@@ -282,9 +265,7 @@ yield resolvedPromise; | ||
...staticListeners.map((listener) => tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (listeners.has(listener)) { | ||
return yield listener(eventArgs); | ||
} | ||
yield this.safeCallListener(eventName, eventArgs, listener); | ||
})), | ||
...staticAnyListeners.map((listener) => tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
if (anyListeners.has(listener)) { | ||
return yield listener(eventName, eventArgs); | ||
yield this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
@@ -295,2 +276,33 @@ })), | ||
} | ||
getListeners(eventName) { | ||
const events = (0, assert_1.assertEx)(Events.eventsMap.get(this)); | ||
if (!events.has(eventName)) { | ||
return; | ||
} | ||
return events.get(eventName); | ||
} | ||
safeCallAnyListener(eventName, eventArgs, listener) { | ||
var _a; | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
try { | ||
return yield listener(eventName, eventArgs); | ||
} | ||
catch (ex) { | ||
const error = ex; | ||
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`Listener[${String(eventName)}] Excepted: ${error.message}`); | ||
} | ||
}); | ||
} | ||
safeCallListener(eventName, eventArgs, listener) { | ||
var _a; | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
try { | ||
return yield listener(eventArgs); | ||
} | ||
catch (ex) { | ||
const error = ex; | ||
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`Listener[${String(eventName)}] Excepted: ${error.message}`); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -300,2 +312,4 @@ exports.Events = Events; | ||
Events.eventsMap = new WeakMap(); | ||
Events.canEmitMetaEvents = false; | ||
Events.isGlobalDebugEnabled = false; | ||
//# sourceMappingURL=Events.js.map |
import { assertEx } from '@xylabs/assert'; | ||
import { forget } from '@xylabs/forget'; | ||
import { Base } from '@xyo-network/core'; | ||
const resolvedPromise = Promise.resolve(); | ||
let canEmitMetaEvents = false; | ||
let isGlobalDebugEnabled = false; | ||
function assertEventName(eventName) { | ||
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') { | ||
throw new TypeError('`eventName` must be a string, symbol, or number'); | ||
} | ||
} | ||
function assertListener(listener) { | ||
if (typeof listener !== 'function') { | ||
throw new TypeError('listener must be a function'); | ||
} | ||
} | ||
const isMetaEvent = (eventName) => eventName === 'listenerAdded' || eventName === 'listenerRemoved'; | ||
export class Events { | ||
export class Events extends Base { | ||
static anyMap = new WeakMap(); | ||
static eventsMap = new WeakMap(); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
debug; | ||
static canEmitMetaEvents = false; | ||
static isGlobalDebugEnabled = false; | ||
//this is here to be able to query the type, not use | ||
eventData = {}; | ||
constructor(options = {}) { | ||
Events.anyMap.set(this, new Set()); | ||
Events.eventsMap.set(this, new Map()); | ||
this.debug = options.debug; | ||
if (this.debug) { | ||
this.debug.enabled = !!this.debug.enabled; | ||
this.debug.logger = | ||
this.debug.logger ?? | ||
constructor(params = {}) { | ||
const mutatedParams = { ...params }; | ||
if (mutatedParams.debug) { | ||
mutatedParams.debug.logger = | ||
mutatedParams.debug.logger ?? | ||
((type, debugName, eventName, eventData) => { | ||
@@ -36,3 +22,2 @@ let eventDataString; | ||
try { | ||
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code. | ||
eventDataString = JSON.stringify(eventData); | ||
@@ -51,5 +36,8 @@ } | ||
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}`; | ||
console.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`); | ||
this.logger.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`); | ||
}); | ||
} | ||
super(mutatedParams); | ||
Events.anyMap.set(this, new Set()); | ||
Events.eventsMap.set(this, new Map()); | ||
} | ||
@@ -60,10 +48,13 @@ static get isDebugEnabled() { | ||
if (typeof globalThis.process?.env !== 'object') { | ||
return isGlobalDebugEnabled; | ||
return Events.isGlobalDebugEnabled; | ||
} | ||
const { env } = globalThis.process ?? { env: {} }; | ||
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled; | ||
return env.DEBUG === 'events' || env.DEBUG === '*' || Events.isGlobalDebugEnabled; | ||
} | ||
static set isDebugEnabled(newValue) { | ||
isGlobalDebugEnabled = newValue; | ||
Events.isGlobalDebugEnabled = newValue; | ||
} | ||
get debug() { | ||
return this.params.debug; | ||
} | ||
clearListeners(eventNames) { | ||
@@ -94,7 +85,7 @@ const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
try { | ||
canEmitMetaEvents = true; | ||
Events.canEmitMetaEvents = true; | ||
await this.emitMetaEventInternal(eventName, eventArgs); | ||
} | ||
finally { | ||
canEmitMetaEvents = false; | ||
Events.canEmitMetaEvents = false; | ||
} | ||
@@ -104,30 +95,33 @@ } | ||
async emitSerial(eventName, eventArgs) { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
} | ||
const filterMatch = (args, filter) => { | ||
if (filter) { | ||
switch (typeof filter) { | ||
case 'object': | ||
return Object.entries(args).reduce((prev, [key, value]) => (filter[key] === value ? true : prev), false); | ||
default: | ||
return args === filter; | ||
} | ||
} | ||
return true; | ||
}; | ||
this.logIfDebugEnabled('emitSerial', eventName, eventArgs); | ||
const listeners = this.getListeners(eventName) ?? new Set(); | ||
const filteredListeners = [...listeners.values()] | ||
.filter((value) => (value.filter ? filterMatch(eventArgs, value.filter) : true)) | ||
.map((info) => info.listener); | ||
const anyListeners = assertEx(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = [...anyListeners]; | ||
await resolvedPromise; | ||
for (const listener of staticListeners) { | ||
if (listeners.has(listener)) { | ||
await listener(eventArgs); | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener); | ||
} | ||
for (const listener of staticAnyListeners) { | ||
if (anyListeners.has(listener)) { | ||
await listener(eventName, eventArgs); | ||
} | ||
await this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
} | ||
getListeners(eventName) { | ||
const events = assertEx(Events.eventsMap.get(this)); | ||
if (!events.has(eventName)) { | ||
return; | ||
} | ||
return events.get(eventName); | ||
} | ||
//TODO: Make test for this | ||
listenerCount(eventNames) { | ||
@@ -141,5 +135,2 @@ const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
} | ||
if (typeof eventName !== 'undefined') { | ||
assertEventName(eventName); | ||
} | ||
count += assertEx(Events.anyMap.get(this)).size; | ||
@@ -158,6 +149,4 @@ for (const value of assertEx(Events.eventsMap.get(this)).values()) { | ||
off(eventNames, listener) { | ||
assertListener(listener); | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName); | ||
const set = this.getListeners(eventName); | ||
@@ -178,3 +167,2 @@ if (set) { | ||
offAny(listener) { | ||
assertListener(listener); | ||
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined); | ||
@@ -185,7 +173,5 @@ const typedMap = Events.anyMap.get(this); | ||
} | ||
on(eventNames, listener) { | ||
assertListener(listener); | ||
on(eventNames, listener, filter) { | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames]; | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName); | ||
let set = this.getListeners(eventName); | ||
@@ -197,3 +183,3 @@ if (!set) { | ||
} | ||
set.add(listener); | ||
set.add({ filter, listener: listener }); | ||
this.logIfDebugEnabled('subscribe', eventName, undefined); | ||
@@ -207,3 +193,2 @@ if (!isMetaEvent(eventName)) { | ||
onAny(listener) { | ||
assertListener(listener); | ||
this.logIfDebugEnabled('subscribeAny', undefined, undefined); | ||
@@ -217,3 +202,3 @@ Events.anyMap.get(this)?.add(listener); | ||
this.off(eventName, subListener); | ||
await listener(args); | ||
await this.safeCallListener(eventName, args, listener); | ||
}; | ||
@@ -223,5 +208,4 @@ this.on(eventName, subListener); | ||
} | ||
async emitInternal(eventName, eventArgs) { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
async emitInternal(eventName, eventArgs, filter) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
@@ -231,4 +215,5 @@ } | ||
const listeners = this.getListeners(eventName) ?? new Set(); | ||
const filteredListeners = [...listeners.values()].filter((value) => (filter ? value.listener : true)).map((info) => info.listener); | ||
const anyListeners = assertEx(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners]; | ||
@@ -238,9 +223,7 @@ await resolvedPromise; | ||
...staticListeners.map(async (listener) => { | ||
if (listeners.has(listener)) { | ||
return await listener(eventArgs); | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener); | ||
}), | ||
...staticAnyListeners.map(async (listener) => { | ||
if (anyListeners.has(listener)) { | ||
return await listener(eventName, eventArgs); | ||
await this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
@@ -251,4 +234,3 @@ }), | ||
async emitMetaEventInternal(eventName, eventArgs) { | ||
assertEventName(eventName); | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`'); | ||
@@ -258,4 +240,5 @@ } | ||
const listeners = this.getListeners(eventName) ?? new Set(); | ||
const filteredListeners = [...listeners.values()].map((info) => info.listener); | ||
const anyListeners = assertEx(Events.anyMap.get(this)); | ||
const staticListeners = [...listeners]; | ||
const staticListeners = [...filteredListeners]; | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners]; | ||
@@ -265,9 +248,7 @@ await resolvedPromise; | ||
...staticListeners.map(async (listener) => { | ||
if (listeners.has(listener)) { | ||
return await listener(eventArgs); | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener); | ||
}), | ||
...staticAnyListeners.map(async (listener) => { | ||
if (anyListeners.has(listener)) { | ||
return await listener(eventName, eventArgs); | ||
await this.safeCallAnyListener(eventName, eventArgs, listener); | ||
} | ||
@@ -277,3 +258,28 @@ }), | ||
} | ||
getListeners(eventName) { | ||
const events = assertEx(Events.eventsMap.get(this)); | ||
if (!events.has(eventName)) { | ||
return; | ||
} | ||
return events.get(eventName); | ||
} | ||
async safeCallAnyListener(eventName, eventArgs, listener) { | ||
try { | ||
return await listener(eventName, eventArgs); | ||
} | ||
catch (ex) { | ||
const error = ex; | ||
this.logger?.error(`Listener[${String(eventName)}] Excepted: ${error.message}`); | ||
} | ||
} | ||
async safeCallListener(eventName, eventArgs, listener) { | ||
try { | ||
return await listener(eventArgs); | ||
} | ||
catch (ex) { | ||
const error = ex; | ||
this.logger?.error(`Listener[${String(eventName)}] Excepted: ${error.message}`); | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=Events.js.map |
@@ -0,1 +1,2 @@ | ||
import { Base, BaseParams } from '@xyo-network/core'; | ||
import { EventAnyListener, EventArgs, EventData, EventFunctions, EventListener, EventName } from '../model'; | ||
@@ -9,17 +10,15 @@ /** | ||
*/ | ||
export type DebugLogger<TEventData extends EventData, TName extends keyof TEventData> = (type: string, debugName: string, eventName?: TName, eventData?: TEventData[TName]) => void; | ||
export type DebugLogger = (type: string, debugName: string, eventName?: EventName, eventData?: EventArgs) => void; | ||
type EventListenerInfo<TEventArgs extends EventArgs = EventArgs> = { | ||
filter?: TEventArgs; | ||
listener: EventListener<TEventArgs>; | ||
}; | ||
/** | ||
Configure debug options of an instance. | ||
*/ | ||
export type DebugOptions<TEventData extends EventData> = { | ||
export type DebugOptions = { | ||
enabled?: boolean; | ||
logger?: DebugLogger<TEventData, keyof TEventData>; | ||
logger?: DebugLogger; | ||
readonly name: string; | ||
}; | ||
/** | ||
Configuration options for Emittery. | ||
*/ | ||
export type Options<TEventData extends EventData> = { | ||
readonly debug?: DebugOptions<TEventData>; | ||
}; | ||
export type MetaEventData<TEventData extends EventData> = { | ||
@@ -35,10 +34,15 @@ listenerAdded: { | ||
}; | ||
export declare class Events<TEventData extends EventData = EventData> implements EventFunctions<TEventData> { | ||
static anyMap: WeakMap<object, Set<EventAnyListener<EventArgs>>>; | ||
static eventsMap: WeakMap<object, Map<PropertyKey, Set<EventListener<EventArgs>>>>; | ||
debug?: DebugOptions<any>; | ||
export type EventsParams = BaseParams<{ | ||
readonly debug?: DebugOptions; | ||
}>; | ||
export declare class Events<TEventData extends EventData = EventData> extends Base<EventsParams> implements EventFunctions<TEventData> { | ||
protected static anyMap: WeakMap<object, Set<EventAnyListener<EventArgs>>>; | ||
protected static eventsMap: WeakMap<object, Map<PropertyKey, Set<EventListenerInfo<EventArgs>>>>; | ||
private static canEmitMetaEvents; | ||
private static isGlobalDebugEnabled; | ||
eventData: TEventData; | ||
constructor(options?: Options<TEventData>); | ||
constructor(params?: EventsParams); | ||
static get isDebugEnabled(): boolean; | ||
static set isDebugEnabled(newValue: boolean); | ||
get debug(): DebugOptions | undefined; | ||
clearListeners(eventNames: keyof TEventData | (keyof TEventData)[]): void; | ||
@@ -48,8 +52,7 @@ emit<TEventName extends keyof TEventData>(eventName: TEventName, eventArgs: TEventData[TEventName]): Promise<void>; | ||
emitSerial<TEventName extends keyof TEventData>(eventName: TEventName, eventArgs: TEventData[TEventName]): Promise<void>; | ||
getListeners(eventName: keyof TEventData): Set<EventListener<EventArgs>> | undefined; | ||
listenerCount(eventNames: keyof TEventData | (keyof TEventData)[]): number; | ||
logIfDebugEnabled<TEventName extends EventName, TEventArgs extends EventArgs>(type: string, eventName?: TEventName, eventArgs?: TEventArgs): void; | ||
off<TEventName extends keyof TEventData>(eventNames: TEventName | TEventName[], listener: EventListener<TEventData[TEventName]>): void; | ||
listenerCount(eventNames?: keyof TEventData | (keyof TEventData)[]): number; | ||
logIfDebugEnabled<TEventName extends EventName>(type: string, eventName?: TEventName, eventArgs?: EventArgs): void; | ||
off<TEventName extends keyof TEventData, TEventListener = EventListener<TEventData[TEventName]>>(eventNames: TEventName | TEventName[], listener: TEventListener): void; | ||
offAny(listener: EventAnyListener): void; | ||
on<TEventName extends keyof TEventData = keyof TEventData>(eventNames: TEventName | TEventName[], listener: EventListener<TEventData[TEventName]>): () => void; | ||
on<TEventName extends keyof TEventData = keyof TEventData>(eventNames: TEventName | TEventName[], listener: EventListener<TEventData[TEventName]>, filter?: TEventData[TEventName]): () => void; | ||
onAny(listener: EventAnyListener): () => void; | ||
@@ -59,3 +62,7 @@ once<TEventName extends keyof TEventData>(eventName: TEventName, listener: EventListener<TEventData[TEventName]>): () => void; | ||
private emitMetaEventInternal; | ||
private getListeners; | ||
private safeCallAnyListener; | ||
private safeCallListener; | ||
} | ||
export {}; | ||
//# sourceMappingURL=Events.d.ts.map |
@@ -15,3 +15,4 @@ { | ||
"@xylabs/forget": "^2.7.4", | ||
"@xyo-network/promise": "^2.53.3" | ||
"@xyo-network/core": "^2.53.4", | ||
"@xyo-network/promise": "^2.53.4" | ||
}, | ||
@@ -57,3 +58,3 @@ "devDependencies": { | ||
"types": "dist/types/index.d.ts", | ||
"version": "2.53.3" | ||
"version": "2.53.4" | ||
} |
import { assertEx } from '@xylabs/assert' | ||
import { forget } from '@xylabs/forget' | ||
import { Base, BaseParams } from '@xyo-network/core' | ||
@@ -13,25 +14,18 @@ import { EventAnyListener, EventArgs, EventData, EventFunctions, EventListener, EventName } from '../model' | ||
*/ | ||
export type DebugLogger<TEventData extends EventData, TName extends keyof TEventData> = ( | ||
type: string, | ||
debugName: string, | ||
eventName?: TName, | ||
eventData?: TEventData[TName], | ||
) => void | ||
export type DebugLogger = (type: string, debugName: string, eventName?: EventName, eventData?: EventArgs) => void | ||
type EventListenerInfo<TEventArgs extends EventArgs = EventArgs> = { | ||
filter?: TEventArgs | ||
listener: EventListener<TEventArgs> | ||
} | ||
/** | ||
Configure debug options of an instance. | ||
*/ | ||
export type DebugOptions<TEventData extends EventData> = { | ||
export type DebugOptions = { | ||
enabled?: boolean | ||
logger?: DebugLogger<TEventData, keyof TEventData> | ||
logger?: DebugLogger | ||
readonly name: string | ||
} | ||
/** | ||
Configuration options for Emittery. | ||
*/ | ||
export type Options<TEventData extends EventData> = { | ||
readonly debug?: DebugOptions<TEventData> | ||
} | ||
const resolvedPromise = Promise.resolve() | ||
@@ -50,45 +44,25 @@ | ||
let canEmitMetaEvents = false | ||
let isGlobalDebugEnabled = false | ||
const isMetaEvent = (eventName: EventName) => eventName === 'listenerAdded' || eventName === 'listenerRemoved' | ||
function assertEventName(eventName: EventName) { | ||
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') { | ||
throw new TypeError('`eventName` must be a string, symbol, or number') | ||
} | ||
} | ||
export type EventsParams = BaseParams<{ readonly debug?: DebugOptions }> | ||
function assertListener(listener: object) { | ||
if (typeof listener !== 'function') { | ||
throw new TypeError('listener must be a function') | ||
} | ||
} | ||
export class Events<TEventData extends EventData = EventData> extends Base<EventsParams> implements EventFunctions<TEventData> { | ||
protected static anyMap = new WeakMap<object, Set<EventAnyListener>>() | ||
protected static eventsMap = new WeakMap<object, Map<EventName, Set<EventListenerInfo>>>() | ||
const isMetaEvent = (eventName: EventName) => eventName === 'listenerAdded' || eventName === 'listenerRemoved' | ||
private static canEmitMetaEvents = false | ||
private static isGlobalDebugEnabled = false | ||
export class Events<TEventData extends EventData = EventData> implements EventFunctions<TEventData> { | ||
static anyMap = new WeakMap<object, Set<EventAnyListener>>() | ||
static eventsMap = new WeakMap<object, Map<EventName, Set<EventListener>>>() | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
debug?: DebugOptions<any> | ||
//this is here to be able to query the type, not use | ||
eventData = {} as TEventData | ||
constructor(options: Options<TEventData> = {}) { | ||
Events.anyMap.set(this, new Set<EventAnyListener>()) | ||
Events.eventsMap.set(this, new Map<keyof TEventData, Set<EventListener>>()) | ||
this.debug = options.debug | ||
if (this.debug) { | ||
this.debug.enabled = !!this.debug.enabled | ||
this.debug.logger = | ||
this.debug.logger ?? | ||
((type: string, debugName: string, eventName?: keyof TEventData, eventData?: TEventData[keyof TEventData]) => { | ||
constructor(params: EventsParams = {}) { | ||
const mutatedParams = { ...params } | ||
if (mutatedParams.debug) { | ||
mutatedParams.debug.logger = | ||
mutatedParams.debug.logger ?? | ||
((type: string, debugName: string, eventName?: EventName, eventData?: EventArgs) => { | ||
let eventDataString: string | ||
let eventNameString: string | undefined | ||
try { | ||
// TODO: Use https://github.com/sindresorhus/safe-stringify when the package is more mature. Just copy-paste the code. | ||
eventDataString = JSON.stringify(eventData) | ||
@@ -107,5 +81,8 @@ } catch { | ||
const logTime = `${currentTime.getHours()}:${currentTime.getMinutes()}:${currentTime.getSeconds()}.${currentTime.getMilliseconds()}` | ||
console.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`) | ||
this.logger.log(`[${logTime}][events:${type}][${debugName}] Event Name: ${eventNameString}\n\tdata: ${eventDataString}`) | ||
}) | ||
} | ||
super(mutatedParams) | ||
Events.anyMap.set(this, new Set<EventAnyListener>()) | ||
Events.eventsMap.set(this, new Map<keyof TEventData, Set<EventListenerInfo>>()) | ||
} | ||
@@ -118,13 +95,17 @@ | ||
if (typeof globalThis.process?.env !== 'object') { | ||
return isGlobalDebugEnabled | ||
return Events.isGlobalDebugEnabled | ||
} | ||
const { env } = globalThis.process ?? { env: {} } | ||
return env.DEBUG === 'emittery' || env.DEBUG === '*' || isGlobalDebugEnabled | ||
return env.DEBUG === 'events' || env.DEBUG === '*' || Events.isGlobalDebugEnabled | ||
} | ||
static set isDebugEnabled(newValue) { | ||
isGlobalDebugEnabled = newValue | ||
Events.isGlobalDebugEnabled = newValue | ||
} | ||
get debug() { | ||
return this.params.debug | ||
} | ||
clearListeners(eventNames: keyof TEventData | (keyof TEventData)[]) { | ||
@@ -159,6 +140,6 @@ const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames] | ||
try { | ||
canEmitMetaEvents = true | ||
Events.canEmitMetaEvents = true | ||
await this.emitMetaEventInternal(eventName, eventArgs) | ||
} finally { | ||
canEmitMetaEvents = false | ||
Events.canEmitMetaEvents = false | ||
} | ||
@@ -169,13 +150,26 @@ } | ||
async emitSerial<TEventName extends keyof TEventData>(eventName: TEventName, eventArgs: TEventData[TEventName]) { | ||
assertEventName(eventName) | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`') | ||
} | ||
const filterMatch = (args: TEventData[TEventName], filter: TEventData[TEventName]) => { | ||
if (filter) { | ||
switch (typeof filter) { | ||
case 'object': | ||
return Object.entries(args).reduce((prev, [key, value]) => ((filter as Record<PropertyKey, unknown>)[key] === value ? true : prev), false) | ||
default: | ||
return args === filter | ||
} | ||
} | ||
return true | ||
} | ||
this.logIfDebugEnabled('emitSerial', eventName, eventArgs) | ||
const listeners = this.getListeners(eventName) ?? new Set() | ||
const filteredListeners = [...listeners.values()] | ||
.filter((value) => (value.filter ? filterMatch(eventArgs, value.filter as TEventData[TEventName]) : true)) | ||
.map((info) => info.listener) | ||
const anyListeners = assertEx(Events.anyMap.get(this)) | ||
const staticListeners = [...listeners] | ||
const staticListeners = [...filteredListeners] | ||
const staticAnyListeners = [...anyListeners] | ||
@@ -186,24 +180,12 @@ | ||
for (const listener of staticListeners) { | ||
if (listeners.has(listener)) { | ||
await listener(eventArgs) | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener) | ||
} | ||
for (const listener of staticAnyListeners) { | ||
if (anyListeners.has(listener)) { | ||
await listener(eventName, eventArgs) | ||
} | ||
await this.safeCallAnyListener(eventName, eventArgs, listener) | ||
} | ||
} | ||
getListeners(eventName: keyof TEventData) { | ||
const events = assertEx(Events.eventsMap.get(this)) | ||
if (!events.has(eventName)) { | ||
return | ||
} | ||
return events.get(eventName) | ||
} | ||
listenerCount(eventNames: keyof TEventData | (keyof TEventData)[]) { | ||
//TODO: Make test for this | ||
listenerCount(eventNames?: keyof TEventData | (keyof TEventData)[]) { | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames] | ||
@@ -219,6 +201,2 @@ let count = 0 | ||
if (typeof eventName !== 'undefined') { | ||
assertEventName(eventName) | ||
} | ||
count += assertEx(Events.anyMap.get(this)).size | ||
@@ -234,3 +212,3 @@ | ||
logIfDebugEnabled<TEventName extends EventName, TEventArgs extends EventArgs>(type: string, eventName?: TEventName, eventArgs?: TEventArgs) { | ||
logIfDebugEnabled<TEventName extends EventName>(type: string, eventName?: TEventName, eventArgs?: EventArgs) { | ||
if (Events.isDebugEnabled || this.debug?.enabled) { | ||
@@ -241,9 +219,9 @@ this.debug?.logger?.(type, this.debug.name, eventName, eventArgs) | ||
off<TEventName extends keyof TEventData>(eventNames: TEventName | TEventName[], listener: EventListener<TEventData[TEventName]>) { | ||
assertListener(listener) | ||
off<TEventName extends keyof TEventData, TEventListener = EventListener<TEventData[TEventName]>>( | ||
eventNames: TEventName | TEventName[], | ||
listener: TEventListener, | ||
) { | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames] | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName) | ||
const set = this.getListeners(eventName) as Set<EventListener<TEventData[TEventName]>> | ||
const set = this.getListeners(eventName) as Set<TEventListener> | ||
if (set) { | ||
@@ -266,4 +244,2 @@ set.delete(listener) | ||
offAny(listener: EventAnyListener) { | ||
assertListener(listener) | ||
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined) | ||
@@ -276,8 +252,9 @@ | ||
on<TEventName extends keyof TEventData = keyof TEventData>(eventNames: TEventName | TEventName[], listener: EventListener<TEventData[TEventName]>) { | ||
assertListener(listener) | ||
on<TEventName extends keyof TEventData = keyof TEventData>( | ||
eventNames: TEventName | TEventName[], | ||
listener: EventListener<TEventData[TEventName]>, | ||
filter?: TEventData[TEventName], | ||
) { | ||
const eventNamesArray = Array.isArray(eventNames) ? eventNames : [eventNames] | ||
for (const eventName of eventNamesArray) { | ||
assertEventName(eventName) | ||
let set = this.getListeners(eventName) | ||
@@ -290,3 +267,3 @@ if (!set) { | ||
set.add(listener as EventListener) | ||
set.add({ filter, listener: listener as EventListener }) | ||
@@ -304,4 +281,2 @@ this.logIfDebugEnabled('subscribe', eventName, undefined) | ||
onAny(listener: EventAnyListener) { | ||
assertListener(listener) | ||
this.logIfDebugEnabled('subscribeAny', undefined, undefined) | ||
@@ -317,3 +292,3 @@ | ||
this.off(eventName, subListener) | ||
await listener(args) | ||
await this.safeCallListener(eventName, args, listener) | ||
} | ||
@@ -327,6 +302,5 @@ this.on(eventName, subListener) | ||
eventArgs: TEventArgs, | ||
filter?: TEventArgs, | ||
) { | ||
assertEventName(eventName) | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`') | ||
@@ -338,4 +312,5 @@ } | ||
const listeners = this.getListeners(eventName) ?? new Set() | ||
const filteredListeners = [...listeners.values()].filter((value) => (filter ? value.listener : true)).map((info) => info.listener) | ||
const anyListeners = assertEx(Events.anyMap.get(this)) | ||
const staticListeners = [...listeners] | ||
const staticListeners = [...filteredListeners] | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners] | ||
@@ -346,9 +321,7 @@ | ||
...staticListeners.map(async (listener) => { | ||
if (listeners.has(listener)) { | ||
return await listener(eventArgs) | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener) | ||
}), | ||
...staticAnyListeners.map(async (listener) => { | ||
if (anyListeners.has(listener)) { | ||
return await listener(eventName, eventArgs) | ||
await this.safeCallAnyListener(eventName, eventArgs, listener) | ||
} | ||
@@ -363,5 +336,3 @@ }), | ||
) { | ||
assertEventName(eventName) | ||
if (isMetaEvent(eventName) && !canEmitMetaEvents) { | ||
if (isMetaEvent(eventName) && !Events.canEmitMetaEvents) { | ||
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`') | ||
@@ -373,4 +344,5 @@ } | ||
const listeners = this.getListeners(eventName) ?? new Set() | ||
const filteredListeners = [...listeners.values()].map((info) => info.listener) | ||
const anyListeners = assertEx(Events.anyMap.get(this)) | ||
const staticListeners = [...listeners] | ||
const staticListeners = [...filteredListeners] | ||
const staticAnyListeners = isMetaEvent(eventName) ? [] : [...anyListeners] | ||
@@ -381,9 +353,7 @@ | ||
...staticListeners.map(async (listener) => { | ||
if (listeners.has(listener)) { | ||
return await listener(eventArgs) | ||
} | ||
await this.safeCallListener(eventName, eventArgs, listener) | ||
}), | ||
...staticAnyListeners.map(async (listener) => { | ||
if (anyListeners.has(listener)) { | ||
return await listener(eventName, eventArgs) | ||
await this.safeCallAnyListener(eventName, eventArgs, listener) | ||
} | ||
@@ -393,2 +363,37 @@ }), | ||
} | ||
private getListeners<TEventName extends keyof TEventData>(eventName: TEventName) { | ||
const events = assertEx(Events.eventsMap.get(this)) | ||
if (!events.has(eventName)) { | ||
return | ||
} | ||
return events.get(eventName) | ||
} | ||
private async safeCallAnyListener<TEventData extends EventData, TEventName extends keyof EventData>( | ||
eventName: TEventName, | ||
eventArgs: TEventData[TEventName], | ||
listener: EventAnyListener<TEventData[TEventName]>, | ||
) { | ||
try { | ||
return await listener(eventName, eventArgs) | ||
} catch (ex) { | ||
const error = ex as Error | ||
this.logger?.error(`Listener[${String(eventName)}] Excepted: ${error.message}`) | ||
} | ||
} | ||
private async safeCallListener<TEventData extends EventData, TEventName extends keyof EventData>( | ||
eventName: TEventName, | ||
eventArgs: TEventData[TEventName], | ||
listener: EventListener<TEventData[TEventName]>, | ||
) { | ||
try { | ||
return await listener(eventArgs) | ||
} catch (ex) { | ||
const error = ex as Error | ||
this.logger?.error(`Listener[${String(eventName)}] Excepted: ${error.message}`) | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
187784
5300
4
+ Added@xyo-network/core@^2.53.4
+ Added@adraffy/ens-normalize@1.10.1(transitive)
+ Added@babel/helper-string-parser@7.25.9(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@babel/types@7.26.0(transitive)
+ Added@noble/curves@1.2.0(transitive)
+ Added@noble/hashes@1.3.2(transitive)
+ Added@scure/base@1.1.9(transitive)
+ Added@types/lodash@4.17.13(transitive)
+ Added@types/lodash-es@4.17.12(transitive)
+ Added@types/node@18.15.13(transitive)
+ Added@xylabs/arraybuffer@3.6.12(transitive)
+ Added@xylabs/assert@3.6.12(transitive)
+ Added@xylabs/error@3.6.12(transitive)
+ Added@xylabs/hex@3.6.12(transitive)
+ Added@xylabs/lodash@3.6.12(transitive)
+ Added@xylabs/logger@3.6.12(transitive)
+ Added@xylabs/object@3.6.12(transitive)
+ Added@xylabs/platform@3.6.12(transitive)
+ Added@xylabs/promise@3.6.12(transitive)
+ Added@xylabs/threads@3.6.12(transitive)
+ Added@xylabs/typeof@3.6.12(transitive)
+ Added@xyo-network/core@2.111.3(transitive)
+ Added@xyo-network/data@2.111.3(transitive)
+ Added@xyo-network/hash@2.111.3(transitive)
+ Added@xyo-network/wasm@2.111.3(transitive)
+ Addedaes-js@4.0.0-beta.5(transitive)
+ Addedcallsites@3.1.0(transitive)
+ Addeddebug@4.3.7(transitive)
+ Addedesm@3.2.25(transitive)
+ Addedethers@6.13.2(transitive)
+ Addedhash-wasm@4.11.0(transitive)
+ Addedis-observable@2.1.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addedobservable-fns@0.6.1(transitive)
+ Addedtiny-worker@2.3.0(transitive)
+ Addedtslib@2.4.0(transitive)
+ Addedwasm-feature-detect@1.8.0(transitive)
+ Addedws@8.17.1(transitive)
Updated@xyo-network/promise@^2.53.4