65 lines
2.5 KiB
JavaScript
65 lines
2.5 KiB
JavaScript
|
|
/* workshop-stream.js — одна SSE-шина Мастерской на всю страницу.
|
|||
|
|
*
|
|||
|
|
* Компоненты не открывают свой EventSource: иначе N компонентов — N соединений,
|
|||
|
|
* и серверный веер умножается. Все подписываются здесь, а этот файл владеет
|
|||
|
|
* единственным соединением и раздаёт каждое событие подписчикам его типа (и «*»).
|
|||
|
|
*
|
|||
|
|
* SSE живёт на поде данных: `/workshop/sse` (Traefik уводит /workshop на сервис
|
|||
|
|
* мастерской). Страница рисуется сайтом eureka-web на общей оболочке.
|
|||
|
|
*
|
|||
|
|
* Использование:
|
|||
|
|
* const off = WorkshopStream.on("noise", (ev) => { ... });
|
|||
|
|
* off(); // отписка
|
|||
|
|
*/
|
|||
|
|
(function () {
|
|||
|
|
const subs = new Map();
|
|||
|
|
// Последнее событие каждого типа, повторяемое поздним подписчикам. Без этого
|
|||
|
|
// обработчики страницы теряют события, случившиеся до их регистрации:
|
|||
|
|
// компоненты апгрейдятся во время загрузки скрипта, и первый brain_ready
|
|||
|
|
// может прийти раньше, чем запустится inline-скрипт страницы.
|
|||
|
|
const last = new Map();
|
|||
|
|
let es = null;
|
|||
|
|
|
|||
|
|
function dispatch(type, ev) {
|
|||
|
|
last.set(type, ev);
|
|||
|
|
const set = subs.get(type);
|
|||
|
|
if (set) for (const fn of set) safe(fn, ev);
|
|||
|
|
const all = subs.get("*");
|
|||
|
|
if (all) for (const fn of all) safe(fn, ev);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function safe(fn, ev) {
|
|||
|
|
try {
|
|||
|
|
fn(ev);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error("[workshop-stream] подписчик упал", err);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function ensure() {
|
|||
|
|
if (es) return;
|
|||
|
|
es = new EventSource("/workshop/sse");
|
|||
|
|
es.onmessage = (m) => {
|
|||
|
|
let ev;
|
|||
|
|
try {
|
|||
|
|
ev = JSON.parse(m.data);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error("[workshop-stream] плохое событие", err);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
dispatch(ev.type, ev);
|
|||
|
|
};
|
|||
|
|
es.onerror = () => {}; // EventSource переподключается сам
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
window.WorkshopStream = {
|
|||
|
|
on(type, fn) {
|
|||
|
|
ensure();
|
|||
|
|
if (!subs.has(type)) subs.set(type, new Set());
|
|||
|
|
subs.get(type).add(fn);
|
|||
|
|
if (last.has(type)) safe(fn, last.get(type));
|
|||
|
|
return () => subs.get(type).delete(fn);
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
})();
|