/** * Küçük olay yayıcı — arayüz (sözleşme) testleri için örnek. * Genel API: * on(event, fn) -> unsubscribe fonksiyonu * once(event, fn) -> unsubscribe fonksiyonu * off(event, fn) -> boolean * emit(event, ...args) -> çağrılan dinleyici sayısı * listenerCount(event) -> number */ class EventEmitter { constructor() { this._listeners = new Map(); } on(event, fn) { if (typeof fn !== 'function') throw new TypeError('listener bir fonksiyon olmalı'); if (!this._listeners.has(event)) this._listeners.set(event, []); this._listeners.get(event).push({ fn, once: false }); return () => this.off(event, fn); } once(event, fn) { if (typeof fn !== 'function') throw new TypeError('listener bir fonksiyon olmalı'); if (!this._listeners.has(event)) this._listeners.set(event, []); this._listeners.get(event).push({ fn, once: true }); return () => this.off(event, fn); } off(event, fn) { const list = this._listeners.get(event); if (!list) return false; const idx = list.findIndex((l) => l.fn === fn); if (idx === -1) return false; list.splice(idx, 1); if (list.length === 0) this._listeners.delete(event); return true; } emit(event, ...args) { const list = this._listeners.get(event); if (!list) return 0; const snapshot = list.slice(); for (const l of snapshot) { if (l.once) this.off(event, l.fn); l.fn(...args); } return snapshot.length; } listenerCount(event) { return (this._listeners.get(event) || []).length; } } module.exports = { EventEmitter };