@supercat1337/event-emitter 1.0.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,21 @@
1
1
  # @supercat1337/event-emitter 🐈⚡
2
2
 
3
- A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and excellent type safety.
3
+ A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript with advanced capabilities and industry-leading type safety.
4
+
5
+ ---
4
6
 
5
7
  ## Features
6
8
 
7
- - ✅ **Full TypeScript support** with generics and complete type definitions
8
- - 🎯 **Promise-based event waiting** with timeout support
9
- - 📊 **Listener lifecycle tracking** - know when events gain/lose listeners
10
- - 🛡️ **Memory safe** - automatic cleanup and unsubscribe functions
11
- - **High performance** - optimized for frequent events
12
- - 🔒 **Immutable patterns** - listener arrays are copied during emission
13
- - 🚀 **Modern ES2022+** - private fields, arrow functions, and more
9
+ - ✅ **Dual Implementation** Choose between lightweight `EventEmitterLite` or full-featured `EventEmitter`.
10
+ - **First-class TypeScript** – Deep generic support for event names and argument validation.
11
+ - **Promise-based Waiting** Native `waitForEvent` and `waitForAnyEvent` with built-in timeout support.
12
+ - **Lifecycle Tracking** Monitor when events gain or lose listeners (`onHasEventListeners`, `onNoEventListeners`).
13
+ - **Centralized Error Handling** Intercept and handle listener errors globally via `onListenerError`.
14
+ - **Memory-Efficient** Automatic cleanup of unused event keys and dedicated `destroy()` lifecycle.
15
+ - **Immutable Emission** Listener arrays are snapshotted during emission, making it safe to modify listeners inside callbacks.
16
+ - ✅ **Modern ES2022+** – Leverages native private fields and optimized logic.
17
+
18
+ ---
14
19
 
15
20
  ## Installation
16
21
 
@@ -18,9 +23,13 @@ A modern, feature-rich EventEmitter implementation for JavaScript and TypeScript
18
23
  npm install @supercat1337/event-emitter
19
24
  ```
20
25
 
26
+ ---
27
+
21
28
  ## Quick Start
22
29
 
23
- ```javascript
30
+ ### Using the full-featured `EventEmitter`
31
+
32
+ ```typescript
24
33
  import { EventEmitter } from '@supercat1337/event-emitter';
25
34
 
26
35
  // Define your event types
@@ -28,7 +37,7 @@ type AppEvents = 'user:created' | 'user:deleted' | 'notification:sent';
28
37
 
29
38
  const emitter = new EventEmitter<AppEvents>();
30
39
 
31
- // Subscribe to events
40
+ // Subscribe with an auto-generated unsubscribe function
32
41
  const unsubscribe = emitter.on('user:created', (userData) => {
33
42
  console.log('User created:', userData);
34
43
  });
@@ -36,224 +45,167 @@ const unsubscribe = emitter.on('user:created', (userData) => {
36
45
  // Emit events
37
46
  emitter.emit('user:created', { id: 1, name: 'John' });
38
47
 
39
- // Unsubscribe when done
48
+ // Clean up
40
49
  unsubscribe();
41
50
  ```
42
51
 
43
- ## API Reference
44
-
45
- ### Core Properties
46
-
47
- #### `isDestroyed`
52
+ ### Using the lightweight `EventEmitterLite`
48
53
 
49
- Is the event emitter destroyed?
54
+ For performance-critical paths where you only need core `on`/`off`/`emit` logic:
50
55
 
51
56
  ```javascript
52
- console.log(emitter.isDestroyed); // false
53
- emitter.destroy();
54
- console.log(emitter.isDestroyed); // true
55
- ```
56
-
57
- #### `events`
58
-
59
- The list of events and their listeners.
57
+ import { EventEmitterLite } from '@supercat1337/event-emitter';
60
58
 
61
- ```javascript
62
- console.log(emitter.events); // { 'user:created': [Function] }
59
+ const lite = new EventEmitterLite();
60
+ lite.on('data', (msg) => console.log(msg));
61
+ lite.emit('data', 'Hello, World!');
63
62
  ```
64
63
 
65
- #### `logErrors`
66
-
67
- Should errors be logged to the console?
68
-
69
- ```javascript
70
- emitter.logErrors = true;
71
- ```
72
-
73
- ### Core Methods
74
-
75
- #### `on(event, listener)`
64
+ ---
76
65
 
77
- Subscribe to an event. Returns an unsubscribe function.
66
+ ## API Reference
78
67
 
79
- ```javascript
80
- const unsubscribe = emitter.on("user:created", (data) => {
81
- console.log(data);
82
- });
68
+ ### Core Classes
83
69
 
84
- // Later...
85
- unsubscribe();
86
- ```
70
+ #### `EventEmitterLite<Events>`
71
+ The core implementation focused on performance. Use this when you don't need async waiting or lifecycle hooks.
87
72
 
88
- #### `off(event, listener)`
73
+ #### `EventEmitter<Events>`
74
+ Extends `EventEmitterLite` with the full suite of advanced features: async promises, tracking hooks, and instance destruction.
89
75
 
90
- Remove a specific listener from an event.
76
+ ### Common Properties
91
77
 
92
- ```javascript
93
- function listener(data) {
94
- /* ... */
95
- }
96
- emitter.on("user:created", listener);
97
- emitter.off("user:created", listener);
98
- ```
78
+ | Property | Type | Description |
79
+ |---------------|-----------|--------------------------------------------------------------------------------------------|
80
+ | `logErrors` | `boolean` | If `true`, errors in listeners are logged to the console. (Default: `true`)<br>Even when `false`, errors can still be caught via `onListenerError`. |
81
+ | `isDestroyed` | `boolean` | (`EventEmitter` only) Returns `true` if the instance has been destroyed. |
99
82
 
100
- #### `emit(event, ...args)`
83
+ ### Common Methods
101
84
 
102
- Emit an event with optional arguments.
85
+ | Method | Description |
86
+ |---------------------------------|---------------------------------------------------------------------------------------|
87
+ | `on(event, listener)` | Subscribes to an event. Returns an `unsubscribe()` function. |
88
+ | `once(event, listener)` | Subscribes for a single invocation, then auto-removes. |
89
+ | `off(event, listener)` | Removes a specific listener. Alias for `removeListener`. |
90
+ | `emit(event, ...args)` | Triggers all listeners for the event with provided arguments. |
91
+ | `removeListener(event, listener)` | Same as `off`. |
103
92
 
104
- ```javascript
105
- emitter.emit("user:created", { id: 1, name: "Alice" });
106
- ```
93
+ ### Advanced Methods (`EventEmitter` only)
107
94
 
108
- #### `once(event, listener)`
95
+ | Method | Description |
96
+ |-------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
97
+ | `waitForEvent(event, [maxWaitMs])` | Returns a `Promise<boolean>`. Resolves `true` when the event fires. If `maxWaitMs` is reached, resolves `false`. |
98
+ | `waitForAnyEvent(events, [maxWaitMs])` | Waits for the first occurring event from an array of event names. |
99
+ | `clear()` | Removes all listeners from all events. |
100
+ | `clearEventListeners(event)` | Removes all listeners for a specific event. |
101
+ | `destroy()` | Completely wipes the instance. Clears all listeners and prevents further emissions. |
102
+ | `onHasEventListeners(callback)` | Invoked when any event gains its **first** listener. Receives the event name. |
103
+ | `onNoEventListeners(callback)` | Invoked when any event loses its **last** listener. Receives the event name. |
104
+ | `onListenerError(callback)` | Invoked when any listener throws an error. Receives `(error, eventName, ...args)`. |
109
105
 
110
- Add a one-time listener that auto-removes after execution.
106
+ #### Example: lifecycle tracking
111
107
 
112
108
  ```javascript
113
- emitter.once("app:ready", () => {
114
- console.log("App is ready!");
109
+ emitter.onHasEventListeners((event) => {
110
+ console.log(`First listener added for ${event}`);
115
111
  });
116
- ```
117
-
118
- ### Advanced Methods
119
-
120
- #### `waitForEvent(event, [maxWaitMs])`
121
-
122
- Wait for an event using Promises.
123
-
124
- ```javascript
125
- // Wait indefinitely
126
- const result = await emitter.waitForEvent("data:loaded");
127
-
128
- // Wait with timeout (returns false if timeout reached)
129
- const success = await emitter.waitForEvent("data:loaded", 5000);
130
- ```
131
-
132
- #### `waitForAnyEvent(events, [maxWaitMs])`
133
-
134
- Wait for any of multiple events.
135
-
136
- ```javascript
137
- const events = ['success', 'error', 'timeout'] as const;
138
- const result = await emitter.waitForAnyEvent(events, 3000);
139
- ```
140
112
 
141
- #### `onHasEventListeners(callback)`
142
-
143
- Get notified when any event gains its first listener.
144
-
145
- ```javascript
146
- emitter.onHasEventListeners((eventName) => {
147
- console.log(`Event ${eventName} now has listeners!`);
113
+ emitter.onNoEventListeners((event) => {
114
+ console.log(`Last listener removed for ${event}`);
148
115
  });
149
- ```
150
-
151
- #### `onNoEventListeners(callback)`
152
-
153
- Get notified when any event loses its last listener.
154
116
 
155
- ```javascript
156
- emitter.onNoEventListeners((eventName) => {
157
- console.log(`Event ${eventName} has no more listeners!`);
158
- });
117
+ emitter.on('foo', () => {}); // logs: First listener added for foo
118
+ emitter.off('foo', () => {}); // logs: Last listener removed for foo
159
119
  ```
160
120
 
161
- #### `onListenerError(callback)`
162
-
163
- Get notified when any listener throws an error.
121
+ #### Example: error handling
164
122
 
165
123
  ```javascript
166
- emitter.onListenerError((error, eventName, ...args) => {
167
- console.error(`Listener for event ${eventName} threw an error:`, error);
124
+ emitter.onListenerError((error, event, ...args) => {
125
+ console.error(`Error in ${event}:`, error);
126
+ myLoggingService.report(error);
168
127
  });
128
+
129
+ emitter.on('crash', () => { throw new Error('boom'); });
130
+ emitter.emit('crash'); // error is caught, passed to the callback, and (if logErrors=true) also logged.
169
131
  ```
170
132
 
171
- ### Lifecycle Management
133
+ ---
172
134
 
173
- #### `destroy()`
135
+ ## TypeScript Usage
174
136
 
175
- Completely destroy the emitter and clean up all resources.
137
+ ### 1. Simple string union
138
+ Good for events that don't pass complex data.
176
139
 
177
- ```javascript
178
- emitter.destroy();
179
- console.log(emitter.isDestroyed); // true
140
+ ```typescript
141
+ type MyEvents = 'start' | 'stop' | 'tick';
142
+ const emitter = new EventEmitter<MyEvents>();
143
+ emitter.emit('start'); // OK
144
+ emitter.emit('unknown'); // Type error
180
145
  ```
181
146
 
182
- #### `clear()`
183
-
184
- Remove all listeners while keeping the emitter functional.
147
+ ### 2. Full type safety (recommended)
148
+ Define a record mapping event names to argument tuples. This gives you autocompletion and argument validation.
185
149
 
186
- ```javascript
187
- emitter.clear();
188
- ```
150
+ ```typescript
151
+ type MyEvents = {
152
+ 'user:updated': [id: number, name: string];
153
+ 'ping': []; // no arguments
154
+ };
189
155
 
190
- #### `clearEventListeners(event)`
156
+ const emitter = new EventEmitter<MyEvents>();
191
157
 
192
- Remove all listeners for a specific event.
158
+ // Listener gets correct argument types
159
+ emitter.on('user:updated', (id, name) => {
160
+ console.log(id, name);
161
+ });
193
162
 
194
- ```javascript
195
- emitter.clearEventListeners("user:created");
163
+ // Emit is type-checked
164
+ emitter.emit('user:updated', 1, 'Alice'); // ✅
165
+ emitter.emit('user:updated', '1'); // ❌ Type error
196
166
  ```
197
167
 
198
- ## TypeScript Usage
168
+ ### 3. Using `keyof` with a separate type map
169
+ If you prefer to keep event names as a union but still want argument types, combine a union with a mapped type:
199
170
 
200
171
  ```typescript
201
- import { EventEmitter } from "@supercat1337/event-emitter";
202
-
203
- // Define your event types
204
- type MyEvents =
205
- | "user:created"
206
- | "user:updated"
207
- | { type: "user:deleted"; payload: { id: string; reason: string } };
172
+ type EventNames = 'start' | 'stop' | 'tick';
173
+ type EventMap = {
174
+ [K in EventNames]: K extends 'tick' ? [counter: number] : [];
175
+ };
208
176
 
209
- const emitter = new EventEmitter<MyEvents>();
210
-
211
- // Full type safety!
212
- emitter.emit("user:created", { id: 1, name: "John" }); // ✅ Correct
213
- emitter.emit("user:created", "invalid"); // ❌ Type error
177
+ const emitter = new EventEmitter<EventMap>();
178
+ emitter.on('tick', (counter) => console.log(counter)); // counter is number
214
179
  ```
215
180
 
216
- ## Error Handling
181
+ ---
217
182
 
218
- All listener errors are caught and logged to console, preventing emitter crashes:
183
+ ## Error Handling
219
184
 
220
- ```javascript
221
- emitter.on("data:received", () => {
222
- throw new Error("Something went wrong!");
223
- });
185
+ By default, all listener errors are caught to prevent the emitter from crashing.
186
+ - If `logErrors` is `true` (default), errors are printed to `console.error`.
187
+ - Even with `logErrors: false`, you can still intercept errors globally using `onListenerError` for custom logging or reporting.
224
188
 
225
- // Error is caught and logged, emitter continues working
226
- emitter.emit("data:received");
227
- ```
189
+ ---
228
190
 
229
191
  ## Performance Notes
230
192
 
231
- - 🔄 Listener arrays are copied before iteration to allow safe modification during emission
232
- - Event existence checks are optimized with direct property access
233
- - 🗑️ Automatic cleanup prevents memory leaks
234
- - 📏 No external dependencies
193
+ - **Snapshotted iteration**: Listener arrays are copied before emission. If a listener calls `off()` on itself during emission, the current cycle continues safely without skipping elements.
194
+ - **Zero dependencies**: Ultra-small bundle size.
195
+ - **Memory management**: Event keys are deleted from the internal store when the last listener is removed, reducing memory usage over time.
196
+
197
+ ---
235
198
 
236
199
  ## Browser Support
237
200
 
238
201
  | Feature | Support |
239
- | ---------- | --------------- |
202
+ |------------|-----------------|
240
203
  | ES2022+ | Modern browsers |
241
204
  | TypeScript | 4.0+ |
242
205
  | Node.js | 14+ |
243
206
 
244
- ## License
245
-
246
- MIT License - feel free to use in commercial projects.
247
-
248
- ## Contributing
249
-
250
- Contributions welcome! Please ensure:
251
-
252
- - ✅ All tests pass
253
- - ✅ TypeScript types are maintained
254
- - ✅ New features include tests
255
- - ✅ Code follows existing style
256
-
257
207
  ---
258
208
 
259
- **Made with ❤️ by [supercat1337](https://github.com/supercat1337)**
209
+ ## License
210
+
211
+ MIT © [supercat1337](https://github.com/supercat1337)
package/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { EventEmitter } from "./dist/event-emitter.esm.js";
2
- //# sourceMappingURL=index.d.ts.map
1
+ export { EventEmitterLite } from "./src/event-emitter-lite.js";
2
+ export { EventEmitter } from "./src/event-emitter.js";
package/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  // @ts-check
2
- export { EventEmitter } from "./dist/event-emitter.esm.js";
2
+ export { EventEmitterLite } from './src/event-emitter-lite.js';
3
+ export { EventEmitter } from './src/event-emitter.js';
package/package.json CHANGED
@@ -1,32 +1,57 @@
1
1
  {
2
2
  "name": "@supercat1337/event-emitter",
3
- "version": "1.0.12",
4
- "description": "Event Emitter",
3
+ "version": "2.0.1",
4
+ "description": "Lightweight typed event emitter with Symbol support, one-time listeners, and zero dependencies. Works in Node.js and browsers.",
5
5
  "main": "index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "types": "./index.d.ts",
12
+ "files": [
13
+ "index.js",
14
+ "index.d.ts",
15
+ "src/**/*.js",
16
+ "src/**/*.d.ts"
17
+ ],
6
18
  "scripts": {
7
19
  "test": "c8 ava",
8
- "build": "npm run remove_type_files && npm run build_esm && npm run build_esm_min && npm run create_types",
9
- "build_esm": "rollup ./src/index.js --file ./dist/event-emitter.esm.js --format es",
10
- "build_esm_min": "esbuild --minify --bundle --platform=neutral --legal-comments=none ./src/index.js --outfile=./dist/event-emitter.esm.min.js",
11
- "create_types": "npx -p typescript tsc --project my.tsconfig.types.json",
12
- "remove_type_files": "del /q *.d.ts *.d.ts.map && cd dist && del /s /q *.d.ts *.d.ts.map && cd ../src && del /s /q *.d.ts *.d.ts.map && cd .."
20
+ "clean": "shx rm -f *.d.ts *.d.ts.map && shx rm -rf src/**/*.d.ts src/**/*.d.ts.map",
21
+ "build:types": "npm run clean && tsc --project my.tsconfig.types.json"
13
22
  },
14
- "author": "Supercat",
23
+ "keywords": [
24
+ "event",
25
+ "emitter",
26
+ "eventemitter",
27
+ "event-emitter",
28
+ "typed",
29
+ "typescript",
30
+ "symbols",
31
+ "lightweight",
32
+ "tiny",
33
+ "zero-dependencies",
34
+ "browser",
35
+ "node",
36
+ "events",
37
+ "pubsub",
38
+ "observer"
39
+ ],
40
+ "author": "Albert Bazaleev",
15
41
  "license": "MIT",
16
42
  "devDependencies": {
43
+ "@types/node": "^20.12.2",
17
44
  "ava": "^6.1.2",
18
45
  "c8": "^9.1.0",
19
- "esbuild": "^0.20.2",
20
- "@types/node": "^20.12.2"
46
+ "shx": "^0.4.0",
47
+ "typescript": "^5.4.3"
21
48
  },
22
- "type": "module",
23
- "moduleResolution": "nodenext",
24
- "keywords": [
25
- "typed event emitter",
26
- "typed event bus"
27
- ],
28
49
  "publishConfig": {
29
50
  "registry": "https://registry.npmjs.org"
30
51
  },
31
- "homepage": "https://github.com/supercat1337/event-emitter"
52
+ "homepage": "https://github.com/supercat1337/event-emitter",
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/supercat1337/event-emitter.git"
56
+ }
32
57
  }
@@ -0,0 +1,52 @@
1
+ export const ORIGINAL: unique symbol;
2
+ /**
3
+ * @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
4
+ */
5
+ export class EventEmitterLite<Events extends string | symbol | Record<string | symbol, any[]> = string> {
6
+ /**
7
+ * @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
8
+ */
9
+ events: any;
10
+ /**
11
+ * logErrors indicates whether errors thrown by listeners should be logged to the console.
12
+ * @type {boolean}
13
+ */
14
+ logErrors: boolean;
15
+ /**
16
+ * on is used to add a callback function that's going to be executed when the event is triggered
17
+ * @template {Events extends string | symbol ? Events : keyof Events} K
18
+ * @param {K} event
19
+ * @param {Function} listener
20
+ * @returns {() => void}
21
+ */
22
+ on<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
23
+ /**
24
+ * Add a one-time listener
25
+ * @template {Events extends string | symbol ? Events : keyof Events} K
26
+ * @param {K} event
27
+ * @param {Function} listener
28
+ * @returns {()=>void}
29
+ */
30
+ once<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
31
+ /**
32
+ * off is an alias for removeListener
33
+ * @template {Events extends string | symbol ? Events : keyof Events} K
34
+ * @param {K} event
35
+ * @param {Function} listener
36
+ */
37
+ off<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
38
+ /**
39
+ * Remove an event listener from an event
40
+ * @template {Events extends string | symbol ? Events : keyof Events} K
41
+ * @param {K} event
42
+ * @param {Function} listener
43
+ */
44
+ removeListener<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
45
+ /**
46
+ * emit is used to trigger an event
47
+ * @template {Events extends string | symbol ? Events : keyof Events} K
48
+ * @param {K} event
49
+ * @param {...any} args
50
+ */
51
+ emit<K extends Events extends string | symbol ? Events : keyof Events>(event: K, ...args: any[]): void;
52
+ }
@@ -0,0 +1,104 @@
1
+ // @ts-check
2
+
3
+ export const ORIGINAL = Symbol('original');
4
+
5
+ /**
6
+ * @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
7
+ */
8
+ export class EventEmitterLite {
9
+ /**
10
+ * @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
11
+ */
12
+ events = Object.create(null);
13
+
14
+ /**
15
+ * logErrors indicates whether errors thrown by listeners should be logged to the console.
16
+ * @type {boolean}
17
+ */
18
+ logErrors = true;
19
+
20
+ /**
21
+ * on is used to add a callback function that's going to be executed when the event is triggered
22
+ * @template {Events extends string | symbol ? Events : keyof Events} K
23
+ * @param {K} event
24
+ * @param {Function} listener
25
+ * @returns {() => void}
26
+ */
27
+ on(event, listener) {
28
+ if (!this.events[event]) this.events[event] = [];
29
+
30
+ this.events[event].push(listener);
31
+ let unsubscriber = () => this.removeListener(event, listener);
32
+ return unsubscriber;
33
+ }
34
+
35
+ /**
36
+ * Add a one-time listener
37
+ * @template {Events extends string | symbol ? Events : keyof Events} K
38
+ * @param {K} event
39
+ * @param {Function} listener
40
+ * @returns {()=>void}
41
+ */
42
+ once(event, listener) {
43
+ const wrapper = (/** @type {...any} */ ...args) => {
44
+ this.removeListener(event, wrapper);
45
+ listener.apply(this, args);
46
+ };
47
+ wrapper[ORIGINAL] = listener;
48
+ return this.on(event, wrapper);
49
+ }
50
+
51
+ /**
52
+ * off is an alias for removeListener
53
+ * @template {Events extends string | symbol ? Events : keyof Events} K
54
+ * @param {K} event
55
+ * @param {Function} listener
56
+ */
57
+ off(event, listener) {
58
+ return this.removeListener(event, listener);
59
+ }
60
+
61
+ /**
62
+ * Remove an event listener from an event
63
+ * @template {Events extends string | symbol ? Events : keyof Events} K
64
+ * @param {K} event
65
+ * @param {Function} listener
66
+ */
67
+ removeListener(event, listener) {
68
+ if (typeof listener !== 'function') return;
69
+
70
+ const listeners = this.events[event];
71
+ if (!listeners) return;
72
+
73
+ // @ts-ignore
74
+ const idx = listeners.findIndex(l => l === listener || l[ORIGINAL] === listener);
75
+ if (idx > -1) {
76
+ listeners.splice(idx, 1);
77
+ if (listeners.length === 0) delete this.events[event];
78
+ }
79
+ }
80
+
81
+ /**
82
+ * emit is used to trigger an event
83
+ * @template {Events extends string | symbol ? Events : keyof Events} K
84
+ * @param {K} event
85
+ * @param {...any} args
86
+ */
87
+ emit(event, ...args) {
88
+ const listeners = this.events[event];
89
+ if (!listeners) return;
90
+
91
+ const queue = (this.events[event] || []).slice();
92
+ var length = queue.length;
93
+
94
+ for (let i = 0; i < length; i++) {
95
+ try {
96
+ queue[i].apply(this, args);
97
+ } catch (e) {
98
+ if (this.logErrors) {
99
+ console.error(`Error in listener for event "${String(event)}":`, e);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }