Skip to content

Commit c07c783

Browse files
authored
Use a different approach to prevent consumers from emitting meta events (#98)
1 parent fb190e1 commit c07c783

File tree

1 file changed

+33
-20
lines changed

1 file changed

+33
-20
lines changed

index.js

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,13 @@ const resolvedPromise = Promise.resolve();
1010
const listenerAdded = Symbol('listenerAdded');
1111
const listenerRemoved = Symbol('listenerRemoved');
1212

13-
// Define a symbol that allows internal code to emit meta events, but prevents userland from doing so.
14-
const metaEventsAllowed = Symbol('metaEventsAllowed');
15-
13+
let canEmitMetaEvents = false;
1614
let isGlobalDebugEnabled = false;
1715

18-
function assertEventName(eventName, allowMetaEvents) {
16+
function assertEventName(eventName) {
1917
if (typeof eventName !== 'string' && typeof eventName !== 'symbol' && typeof eventName !== 'number') {
2018
throw new TypeError('`eventName` must be a string, symbol, or number');
2119
}
22-
23-
if (isMetaEvent(eventName) && allowMetaEvents !== metaEventsAllowed) {
24-
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
25-
}
2620
}
2721

2822
function assertListener(listener) {
@@ -157,6 +151,17 @@ function defaultMethodNamesOrAssert(methodNames) {
157151

158152
const isMetaEvent = eventName => eventName === listenerAdded || eventName === listenerRemoved;
159153

154+
function emitMetaEvent(emitter, eventName, eventData) {
155+
if (isMetaEvent(eventName)) {
156+
try {
157+
canEmitMetaEvents = true;
158+
emitter.emit(eventName, eventData);
159+
} finally {
160+
canEmitMetaEvents = false;
161+
}
162+
}
163+
}
164+
160165
class Emittery {
161166
static mixin(emitteryPropertyName, methodNames) {
162167
methodNames = defaultMethodNamesOrAssert(methodNames);
@@ -253,13 +258,13 @@ class Emittery {
253258

254259
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
255260
for (const eventName of eventNames) {
256-
assertEventName(eventName, metaEventsAllowed);
261+
assertEventName(eventName);
257262
getListeners(this, eventName).add(listener);
258263

259264
this.logIfDebugEnabled('subscribe', eventName, undefined);
260265

261266
if (!isMetaEvent(eventName)) {
262-
this.emit(listenerAdded, {eventName, listener}, metaEventsAllowed);
267+
emitMetaEvent(this, listenerAdded, {eventName, listener});
263268
}
264269
}
265270

@@ -271,13 +276,13 @@ class Emittery {
271276

272277
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
273278
for (const eventName of eventNames) {
274-
assertEventName(eventName, metaEventsAllowed);
279+
assertEventName(eventName);
275280
getListeners(this, eventName).delete(listener);
276281

277282
this.logIfDebugEnabled('unsubscribe', eventName, undefined);
278283

279284
if (!isMetaEvent(eventName)) {
280-
this.emit(listenerRemoved, {eventName, listener}, metaEventsAllowed);
285+
emitMetaEvent(this, listenerRemoved, {eventName, listener});
281286
}
282287
}
283288
}
@@ -299,14 +304,18 @@ class Emittery {
299304
events(eventNames) {
300305
eventNames = Array.isArray(eventNames) ? eventNames : [eventNames];
301306
for (const eventName of eventNames) {
302-
assertEventName(eventName, metaEventsAllowed);
307+
assertEventName(eventName);
303308
}
304309

305310
return iterator(this, eventNames);
306311
}
307312

308-
async emit(eventName, eventData, allowMetaEvents) {
309-
assertEventName(eventName, allowMetaEvents);
313+
async emit(eventName, eventData) {
314+
assertEventName(eventName);
315+
316+
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
317+
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
318+
}
310319

311320
this.logIfDebugEnabled('emit', eventName, eventData);
312321

@@ -332,8 +341,12 @@ class Emittery {
332341
]);
333342
}
334343

335-
async emitSerial(eventName, eventData, allowMetaEvents) {
336-
assertEventName(eventName, allowMetaEvents);
344+
async emitSerial(eventName, eventData) {
345+
assertEventName(eventName);
346+
347+
if (isMetaEvent(eventName) && !canEmitMetaEvents) {
348+
throw new TypeError('`eventName` cannot be meta event `listenerAdded` or `listenerRemoved`');
349+
}
337350

338351
this.logIfDebugEnabled('emitSerial', eventName, eventData);
339352

@@ -364,7 +377,7 @@ class Emittery {
364377
this.logIfDebugEnabled('subscribeAny', undefined, undefined);
365378

366379
anyMap.get(this).add(listener);
367-
this.emit(listenerAdded, {listener}, metaEventsAllowed);
380+
emitMetaEvent(this, listenerAdded, {listener});
368381
return this.offAny.bind(this, listener);
369382
}
370383

@@ -377,7 +390,7 @@ class Emittery {
377390

378391
this.logIfDebugEnabled('unsubscribeAny', undefined, undefined);
379392

380-
this.emit(listenerRemoved, {listener}, metaEventsAllowed);
393+
emitMetaEvent(this, listenerRemoved, {listener});
381394
anyMap.get(this).delete(listener);
382395
}
383396

@@ -427,7 +440,7 @@ class Emittery {
427440
}
428441

429442
if (typeof eventName !== 'undefined') {
430-
assertEventName(eventName, metaEventsAllowed);
443+
assertEventName(eventName);
431444
}
432445

433446
count += anyMap.get(this).size;

0 commit comments

Comments
 (0)