window.lanternBus — a tiny pub/sub layer on top of document custom events. Any script or component can register for a named event; any other can fire it. The bus adds listener counting and metadata on top of the plain addEventListener substrate so feature-detection UIs can reactively surface controls only when a handler is present.
lantern/public/js/lantern-bus.js
| Method | Purpose |
|---|---|
on(name, handler, { meta } = {}) |
Register. Returns an unsubscribe function. |
off(name, handler) |
Unregister. |
emit(name, detail) |
Fire a CustomEvent(name, { detail }) on document. |
hasListeners(name) |
True if at least one bus-registered handler exists. |
listeners(name) |
[{ meta }, ...] — inspect who's listening (registration objects, no handlers exposed). |
Plain document.addEventListener / dispatchEvent remain compatible — they just aren't counted. Migrate to bus.on when introspection matters.
Components render conditional chrome based on who else is on the page.
if (window.lanternBus.hasListeners('transfer-to-sidebar')) {
// Another component is listening — surface the "move to sidebar" button.
}
Meta-events bus:listener-added and bus:listener-removed fire on every registration change, letting UIs stay in sync without polling.
When a wanderland-core pipeline emits a :signals:web:<action> crossing, the initiating component walks the response's events array, strips the :signals:web: prefix, and calls bus.emit(action, payload). See wanderland-web-signals for the full contract and receiver library.
for (const ev of response.events || []) {
if (!ev.type_addr.startsWith(':signals:web:')) continue;
const name = ev.type_addr.slice(':signals:web:'.length);
window.lanternBus.emit(name, ev.payload);
}
Every subscriber for name receives the payload. The payload carries whatever routing the action needs (target element id, slug, section, message body, form fields). No component owns the action namespace — receivers self-register and decide what a given action means.