eureka-web/assets/workshop/workshop-stream.js
eureka cc3d6c5ca0 workshop: страница в общей оболочке сайта + компоненты
Мастерская переехала на eureka2: страница /workshop — часть сайта (lib/workshop.js,
общая шапка/навигация/подвал), оформление в tokens сайта (assets/workshop/
workshop.css), компоненты читают данные пода по SSE /workshop/sse.
Компоненты перенесены на сторону сайта, геометрия мозга — assets/workshop/.
build.js собирает страницу (active: workshop).
2026-10-04 01:08:06 +03:00

64 lines
2.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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);
},
};
})();