Files
newbury-exhibit-v2/public/js/net.js
T

52 lines
2.0 KiB
JavaScript

/* net.js — WebSocket sync client + dev-mode error pipeline. */
export class ExhibitNet {
constructor() {
this.handlers = new Map();
this.ws = null;
this.retry = 1000;
}
on(type, fn) { this.handlers.set(type, fn); return this; }
connect() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
this.ws = new WebSocket(`${proto}://${location.host}/ws`);
this.ws.onopen = () => { this.retry = 1000; };
this.ws.onmessage = (ev) => {
let m; try { m = JSON.parse(ev.data); } catch { return; }
const h = this.handlers.get(m.type);
if (h) h(m);
};
this.ws.onclose = () => {
setTimeout(() => this.connect(), this.retry);
this.retry = Math.min(this.retry * 2, 15000);
};
}
}
/* Dev-mode error checking: capture JS errors + unhandled rejections, show an
* on-screen overlay, and report to the server for the admin error log. */
export function installErrorReporter(devMode) {
const report = (payload) => {
fetch('/api/client-error', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ua: navigator.userAgent, page: location.pathname, ...payload }),
}).catch(() => {});
if (devMode) showOverlay(payload);
};
window.addEventListener('error', (e) =>
report({ kind: 'error', msg: String(e.message), src: e.filename, line: e.lineno }));
window.addEventListener('unhandledrejection', (e) =>
report({ kind: 'rejection', msg: String(e.reason && e.reason.message || e.reason) }));
return report;
}
let overlayEl = null;
function showOverlay(p) {
if (!overlayEl) {
overlayEl = document.createElement('div');
overlayEl.style.cssText = 'position:fixed;bottom:0;left:0;right:0;max-height:35vh;overflow:auto;' +
'background:rgba(120,0,0,.88);color:#fff;font:12px monospace;padding:8px;z-index:99999;white-space:pre-wrap';
document.body.appendChild(overlayEl);
}
overlayEl.textContent += `[${p.kind}] ${p.msg}${p.src ? ` (${p.src}:${p.line})` : ''}\n`;
}