tarinai/js/event_bus.js
2026-07-05 18:01:36 +09:00

52 lines
1.6 KiB
JavaScript

"use strict";
const TARINAI_PUBLIC_EVENT_TYPES = new Set(["audio:play", "selection:changed", "tool:selected", "world:phase", "log:entry", "tool:placed"]);
class TarinaiEventBus {
constructor() {
this.handlers = new Map();
}
on(type, handler) {
if (!type || typeof handler !== "function") return;
let list = this.handlers.get(type);
if (!list) {
list = new Set();
this.handlers.set(type, list);
}
list.add(handler);
}
hasDirectListeners(type) {
if (!type) return false;
return Boolean(this.handlers.get(type)?.size || 0);
}
hasWildcardListeners(type) {
if (!type || !TARINAI_PUBLIC_EVENT_TYPES.has(String(type))) return false;
return Boolean(this.handlers.get("*")?.size || 0);
}
hasListeners(type) {
if (!type) return false;
return Boolean(this.hasDirectListeners(type) || this.hasWildcardListeners(type));
}
emit(type, detail = {}) {
if (!type) return null;
const direct = this.handlers.get(type);
const wildcard = TARINAI_PUBLIC_EVENT_TYPES.has(String(type)) ? this.handlers.get("*") : null;
if (!(direct?.size || 0) && !(wildcard?.size || 0)) return null;
const event = { type, detail: detail || {}, at: performance?.now?.() ?? Date.now() };
if (direct) for (const handler of [...direct]) this.safeCall(handler, event);
if (wildcard) for (const handler of [...wildcard]) this.safeCall(handler, event);
return event;
}
safeCall(handler, event) {
try { handler(event); }
catch (err) { console.error("event handler failed", event.type, err); }
}
}
window.TarinaiEvents = window.TarinaiEvents || new TarinaiEventBus();